From b3cac6858a618df4a56c6d788e57269e6b85c835 Mon Sep 17 00:00:00 2001 From: Michael D Toguchi Date: Fri, 24 Jul 2026 11:37:11 -0700 Subject: [PATCH 01/11] [Driver][NFC] Fix devicelib dependencies for new offload tests Some new offload model tests within sycl-offload-jit.cpp will fail when the corresponding device libraries are not built. In those cases, the test needs to pick up the internal variants so the driver does not error due to not finding any. --- clang/test/Driver/sycl-offload-jit.cpp | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/clang/test/Driver/sycl-offload-jit.cpp b/clang/test/Driver/sycl-offload-jit.cpp index 397dd579c470c..92cc405fdddfa 100644 --- a/clang/test/Driver/sycl-offload-jit.cpp +++ b/clang/test/Driver/sycl-offload-jit.cpp @@ -31,7 +31,7 @@ // Check if path to libsycl.so is passed to clang-linker-wrapper tool by default for SYCL compilation. // The test also checks if SYCL header include paths are added to the SYCL host and device compilation. -// INTEL-RUN: %clang --offload-new-driver --sysroot=%S/Inputs/SYCL -### --target=x86_64-unknown-linux-gnu -fsycl %s 2>&1 \ +// RUN: %clang --offload-new-driver --sysroot=%S/Inputs/SYCL -### --target=x86_64-unknown-linux-gnu -fsycl %s 2>&1 \ // RUN: | FileCheck -check-prefixes=CHECK-LSYCL,CHECK-SYCL-HEADERS-HOST,CHECK-SYCL-HEADERS-DEVICE %s // CHECK-SYCL-HEADERS-DEVICE: "-fsycl-is-device"{{.*}} "-internal-isystem" "{{.*}}bin{{[/\\]+}}..{{[/\\]+}}include" // CHECK-SYCL-HEADERS-HOST: "-fsycl-is-host"{{.*}} "-internal-isystem" "{{.*}}bin{{[/\\]+}}..{{[/\\]+}}include" @@ -63,27 +63,27 @@ /// Check -fsycl-device-image-split= is forwarded to clang-sycl-linker as the /// corresponding --module-split-mode= value. -// RUN: %clang --offload-new-driver -### --target=x86_64-unknown-linux-gnu -fsycl -fsycl-device-image-split=kernel %s 2>&1 \ +// RUN: %clang --offload-new-driver --sysroot=%S/Inputs/SYCL -### --target=x86_64-unknown-linux-gnu -fsycl -fsycl-device-image-split=kernel %s 2>&1 \ // RUN: | FileCheck -check-prefix=CHK-SPLIT-KERNEL %s // CHK-SPLIT-KERNEL: clang-linker-wrapper{{.*}}"--device-linker=spir64-unknown-unknown=--module-split-mode=kernel" -// RUN: %clang --offload-new-driver -### --target=x86_64-unknown-linux-gnu -fsycl -fsycl-device-image-split=translation_unit %s 2>&1 \ +// RUN: %clang --offload-new-driver --sysroot=%S/Inputs/SYCL -### --target=x86_64-unknown-linux-gnu -fsycl -fsycl-device-image-split=translation_unit %s 2>&1 \ // RUN: | FileCheck -check-prefix=CHK-SPLIT-TU %s // CHK-SPLIT-TU: clang-linker-wrapper{{.*}}"--device-linker=spir64-unknown-unknown=--module-split-mode=translation_unit" -// RUN: %clang --offload-new-driver -### --target=x86_64-unknown-linux-gnu -fsycl -fsycl-device-image-split=link_unit %s 2>&1 \ +// RUN: %clang --offload-new-driver --sysroot=%S/Inputs/SYCL -### --target=x86_64-unknown-linux-gnu -fsycl -fsycl-device-image-split=link_unit %s 2>&1 \ // RUN: | FileCheck -check-prefix=CHK-SPLIT-LU %s // CHK-SPLIT-LU: clang-linker-wrapper{{.*}}"--device-linker=spir64-unknown-unknown=--module-split-mode=link_unit" /// Check the bare -fsycl-device-image-split flag aliases to 'translation_unit'. -// RUN: %clang --offload-new-driver -### --target=x86_64-unknown-linux-gnu -fsycl -fsycl-device-image-split %s 2>&1 \ +// RUN: %clang --offload-new-driver --sysroot=%S/Inputs/SYCL -### --target=x86_64-unknown-linux-gnu -fsycl -fsycl-device-image-split %s 2>&1 \ // RUN: | FileCheck -check-prefix=CHK-SPLIT-TU %s /// Check that without -fsycl-device-image-split, no --module-split-mode= is passed. -// RUN: %clang --offload-new-driver -### --target=x86_64-unknown-linux-gnu -fsycl %s 2>&1 \ +// RUN: %clang --offload-new-driver --sysroot=%S/Inputs/SYCL -### --target=x86_64-unknown-linux-gnu -fsycl %s 2>&1 \ // RUN: | FileCheck -check-prefix=CHK-NO-SPLIT %s // CHK-NO-SPLIT-NOT: --module-split-mode= /// Check an invalid -fsycl-device-image-split= value is diagnosed. -// RUN: not %clang --offload-new-driver -### --target=x86_64-unknown-linux-gnu -fsycl -fsycl-device-image-split=bogus %s 2>&1 \ +// RUN: not %clang --offload-new-driver --sysroot=%S/Inputs/SYCL -### --target=x86_64-unknown-linux-gnu -fsycl -fsycl-device-image-split=bogus %s 2>&1 \ // RUN: | FileCheck -check-prefix=CHK-SPLIT-INVALID %s // CHK-SPLIT-INVALID: error: invalid value 'bogus' in '-fsycl-device-image-split=' @@ -95,6 +95,6 @@ /// Check for option incompatibility with -fsycl // RUN: not %clang -### -fsycl -ffreestanding %s 2>&1 \ // RUN: | FileCheck -check-prefix=CHK-INCOMPATIBILITY %s -DINCOMPATOPT=-ffreestanding -// RUN: not %clang -### -fsycl --offload-new-driver -static-libstdc++ %s 2>&1 \ +// RUN: not %clang --sysroot=%S/Inputs/SYCL -### -fsycl --offload-new-driver -static-libstdc++ %s 2>&1 \ // RUN: | FileCheck -check-prefix=CHK-INCOMPATIBILITY %s -DINCOMPATOPT=-static-libstdc++ // CHK-INCOMPATIBILITY: error: invalid argument '[[INCOMPATOPT]]' not allowed with '-fsycl' From 6bc28b28eaad9dfee34ef1e08dca705cbe5113e6 Mon Sep 17 00:00:00 2001 From: Michael D Toguchi Date: Mon, 10 Aug 2026 12:22:05 -0700 Subject: [PATCH 02/11] [SYCL][Driver] Add option to specify the location of ocloc The ocloc tool used for ahead of time compilation targeting Intel GPUs is acquired externally, so it is not guaranteed to be placed in the PATH. Add --ocloc-path=, allowing a user to specify the directory in which ocloc resides. The user provided location always wins over any ocloc that is otherwise visible via the program paths or the PATH environment variable. The option is honored for both the old and the new offloading model: - Old model: the driver's Intel GPU backend compile job (SYCL.cpp) and the ocloc help emitted for -fsycl-help=gen. - New model: forwarded to clang-linker-wrapper, and to clang-sycl-linker for the --sycl-link path. Both tools also accept --ocloc-path= directly for standalone use. --- clang/include/clang/Options/Options.td | 5 ++ clang/lib/Driver/Driver.cpp | 10 ++- clang/lib/Driver/ToolChains/Clang.cpp | 5 ++ clang/lib/Driver/ToolChains/SPIRV.cpp | 5 ++ clang/lib/Driver/ToolChains/SYCL.cpp | 17 ++++- clang/lib/Driver/ToolChains/SYCL.h | 5 ++ clang/test/Driver/clang-linker-wrapper.cpp | 11 ++++ clang/test/Driver/sycl-ocloc-path.cpp | 65 +++++++++++++++++++ .../OffloadTools/clang-sycl-linker/basic.ll | 13 ++++ .../ClangLinkerWrapper.cpp | 24 ++++++- .../clang-linker-wrapper/LinkerWrapperOpts.td | 4 ++ .../clang-sycl-linker/ClangSYCLLinker.cpp | 22 ++++++- clang/tools/clang-sycl-linker/SYCLLinkOpts.td | 5 ++ 13 files changed, 182 insertions(+), 9 deletions(-) create mode 100644 clang/test/Driver/sycl-ocloc-path.cpp diff --git a/clang/include/clang/Options/Options.td b/clang/include/clang/Options/Options.td index 837407f0ba574..ac8179df76054 100644 --- a/clang/include/clang/Options/Options.td +++ b/clang/include/clang/Options/Options.td @@ -7903,6 +7903,11 @@ def fsycl_help_EQ def fsycl_help : Flag<["-"], "fsycl-help">, Alias, Flags<[NoXarchOption]>, AliasArgs<["all"]>, HelpText<"Emit help information from all of the offline compilation tools">; +def ocloc_path_EQ : Joined<["--"], "ocloc-path=">, + Visibility<[ClangOption, CLOption]>, + Flags<[NoXarchOption, NoArgumentUnused]>, MetaVarName<"">, + HelpText<"Directory containing the ocloc tool, which is used for ahead of " + "time compilation targeting Intel GPUs.">; def fsycl_libspirv_path_EQ : Joined<["-"], "fsycl-libspirv-path=">, HelpText<"Path to libspirv library">; def fno_sycl_libspirv : Flag<["-"], "fno-sycl-libspirv">, diff --git a/clang/lib/Driver/Driver.cpp b/clang/lib/Driver/Driver.cpp index 7a0d81674601d..e21bbfa70f9fc 100644 --- a/clang/lib/Driver/Driver.cpp +++ b/clang/lib/Driver/Driver.cpp @@ -2830,8 +2830,14 @@ void Driver::PrintSYCLToolHelp(const Compilation &C) const { llvm::outs().flush(); std::vector ToolArgs = {std::get<1>(HA), std::get<2>(HA), std::get<3>(HA)}; - SmallString<128> ExecPath( - C.getDefaultToolChain().GetProgramPath(std::get<1>(HA).data())); + SmallString<128> ExecPath; + // A user provided --ocloc-path= overrides the usual tool lookup for ocloc. + if (Arg *A = C.getArgs().getLastArg(options::OPT_ocloc_path_EQ); + A && std::get<1>(HA) == "ocloc") { + ExecPath = A->getValue(); + llvm::sys::path::append(ExecPath, std::get<1>(HA)); + } else + ExecPath = C.getDefaultToolChain().GetProgramPath(std::get<1>(HA).data()); // do not run the tools with -###. if (C.getArgs().hasArg(options::OPT__HASH_HASH_HASH)) { llvm::errs() << "\"" << ExecPath << "\" \"" << ToolArgs[1] << "\""; diff --git a/clang/lib/Driver/ToolChains/Clang.cpp b/clang/lib/Driver/ToolChains/Clang.cpp index 15b81a8f94adc..dfa1452bf1ae7 100644 --- a/clang/lib/Driver/ToolChains/Clang.cpp +++ b/clang/lib/Driver/ToolChains/Clang.cpp @@ -12151,6 +12151,11 @@ void LinkerWrapper::ConstructJob(Compilation &C, const JobAction &JA, // Add any SYCL offloading specific options to the clang-linker-wrapper if (C.hasOffloadToolChain()) { + // Forward the user provided location along to the tools that call ocloc. + if (Arg *A = Args.getLastArg(options::OPT_ocloc_path_EQ)) + CmdArgs.push_back( + Args.MakeArgString(Twine("--ocloc-path=") + A->getValue())); + if (Args.hasArg(options::OPT_fsycl_link_EQ)) CmdArgs.push_back(Args.MakeArgString("--sycl-device-link")); diff --git a/clang/lib/Driver/ToolChains/SPIRV.cpp b/clang/lib/Driver/ToolChains/SPIRV.cpp index 5cc1eec74c1cc..4f577e367e820 100644 --- a/clang/lib/Driver/ToolChains/SPIRV.cpp +++ b/clang/lib/Driver/ToolChains/SPIRV.cpp @@ -186,6 +186,11 @@ void SPIRV::Linker::ConstructJob(Compilation &C, const JobAction &JA, Linker = ToolChain.GetProgramPath("clang-sycl-linker"); if (Args.hasArg(options::OPT_v)) CmdArgs.push_back("-v"); + // ocloc is acquired externally, so it is not guaranteed to be in the PATH. + // Forward the user provided location along to the AOT compilation step. + if (Arg *A = Args.getLastArg(options::OPT_ocloc_path_EQ)) + CmdArgs.push_back( + Args.MakeArgString(Twine("--ocloc-path=") + A->getValue())); } else if (!llvm::sys::fs::can_execute(Linker) && !C.getArgs().hasArg(clang::options::OPT__HASH_HASH_HASH)) { C.getDriver().Diag(clang::diag::err_drv_no_spv_tools) << getShortName(); diff --git a/clang/lib/Driver/ToolChains/SYCL.cpp b/clang/lib/Driver/ToolChains/SYCL.cpp index 084cf7614158b..840584225fc70 100644 --- a/clang/lib/Driver/ToolChains/SYCL.cpp +++ b/clang/lib/Driver/ToolChains/SYCL.cpp @@ -1054,6 +1054,19 @@ static const char *makeExeName(Compilation &C, StringRef Name) { return C.getArgs().MakeArgString(ExeName); } +const char *SYCL::gen::getOclocPath(Compilation &C, const ToolChain &TC, + const llvm::opt::ArgList &Args) { + const char *ExeName = makeExeName(C, "ocloc"); + // A user provided --ocloc-path= takes precedence over any ocloc that is + // found via the program paths or the PATH environment variable. + if (Arg *A = Args.getLastArg(options::OPT_ocloc_path_EQ)) { + SmallString<128> OclocPath(A->getValue()); + llvm::sys::path::append(OclocPath, ExeName); + return C.getArgs().MakeArgString(OclocPath); + } + return C.getArgs().MakeArgString(TC.GetProgramPath(ExeName)); +} + // Determine if any of the given arguments contain any PVC based values for // the -device option. static bool hasPVCDevice(const ArgStringList &CmdArgs, std::string &DevArg) { @@ -1117,9 +1130,7 @@ void SYCL::gen::BackendCompiler::ConstructJob(Compilation &C, Device); TC.TranslateLinkerTargetArgs(getToolChain().getTriple(), Args, CmdArgs, Device); - SmallString<128> ExecPath( - getToolChain().GetProgramPath(makeExeName(C, "ocloc"))); - const char *Exec = C.getArgs().MakeArgString(ExecPath); + const char *Exec = SYCL::gen::getOclocPath(C, getToolChain(), Args); auto Cmd = std::make_unique(JA, *this, ResponseFileSupport::None(), Exec, CmdArgs, ArrayRef{}); if (!ForeachInputs.empty()) { diff --git a/clang/lib/Driver/ToolChains/SYCL.h b/clang/lib/Driver/ToolChains/SYCL.h index 777c1193c260f..312c345c645ae 100644 --- a/clang/lib/Driver/ToolChains/SYCL.h +++ b/clang/lib/Driver/ToolChains/SYCL.h @@ -82,6 +82,11 @@ StringRef resolveGenDevice(StringRef DeviceName); SmallString<64> getGenDeviceMacro(StringRef DeviceName); StringRef getGenGRFFlag(StringRef GRFMode); +// Returns the full path of the ocloc tool to be used for AOT compilation. A +// user provided --ocloc-path= is honored above all other lookup locations. +const char *getOclocPath(Compilation &C, const ToolChain &TC, + const llvm::opt::ArgList &Args); + // Prefix for GPU specific targets used for -fsycl-targets constexpr char IntelGPU[] = "intel_gpu_"; constexpr char NvidiaGPU[] = "nvidia_gpu_"; diff --git a/clang/test/Driver/clang-linker-wrapper.cpp b/clang/test/Driver/clang-linker-wrapper.cpp index 9c20a19601bee..c9dd6a4e285e9 100644 --- a/clang/test/Driver/clang-linker-wrapper.cpp +++ b/clang/test/Driver/clang-linker-wrapper.cpp @@ -138,6 +138,17 @@ // CHK-NO-CMDS-AOT-GEN-LINKERARG: sycl-post-link{{.*}} -o {{[^,]*}}.table {{.*}}.bc // CHK-NO-CMDS-AOT-GEN-LINKERARG: ocloc{{.*}} -device pvc -output +// Check that --ocloc-path= provides the location of the ocloc tool. +// RUN: clang-linker-wrapper --ocloc-path=/my/ocloc/dir --linker-path=/usr/bin/ld -o /dev/null %t1.o --dry-run 2>&1 | FileCheck -check-prefix=CHK-OCLOC-PATH %s +// CHK-OCLOC-PATH: "/my/ocloc/dir{{[/\\]+}}ocloc" -output_no_suffix +// Check that --ocloc-path= is not forwarded on to the host linker. +// CHK-OCLOC-PATH-NOT: ld{{.*}} --ocloc-path= + +// Check the diagnostic emitted when ocloc cannot be found in the given +// directory. +// RUN: not clang-linker-wrapper --ocloc-path=%t.no-ocloc-here --linker-path=/usr/bin/ld -o /dev/null %t1.o 2>&1 | FileCheck -check-prefix=CHK-OCLOC-PATH-ERR %s +// CHK-OCLOC-PATH-ERR: Unable to find 'ocloc' in '{{.*}}no-ocloc-here' + /// Check for list of commands for standalone clang-linker-wrapper run for sycl (AOT for Intel CPU) // ------- // Generate .o file as linker wrapper input. diff --git a/clang/test/Driver/sycl-ocloc-path.cpp b/clang/test/Driver/sycl-ocloc-path.cpp new file mode 100644 index 0000000000000..007e97aa23b1c --- /dev/null +++ b/clang/test/Driver/sycl-ocloc-path.cpp @@ -0,0 +1,65 @@ +/// +/// Tests for --ocloc-path=, which provides the location of the externally +/// acquired ocloc tool used for Intel GPU AOT compilation. +/// + +// REQUIRES: x86-registered-target + +/// Check that --ocloc-path= is used for the old offloading model. +// RUN: %clang -### -fsycl --no-offload-new-driver -fsycl-targets=spir64_gen \ +// RUN: --ocloc-path=/my/ocloc/dir %s 2>&1 \ +// RUN: | FileCheck -check-prefix=CHK-OCLOC-PATH-OLD %s +// RUN: %clang -### -fsycl --no-offload-new-driver \ +// RUN: -fsycl-targets=intel_gpu_pvc --ocloc-path=/my/ocloc/dir %s 2>&1 \ +// RUN: | FileCheck -check-prefix=CHK-OCLOC-PATH-OLD %s +// CHK-OCLOC-PATH-OLD: "/my/ocloc/dir{{[/\\]+}}ocloc" "-output" + +/// Check that the user provided location wins over an ocloc that is visible +/// via the PATH. +// RUN: mkdir -p %t.dir +// RUN: touch %t.dir/ocloc +// RUN: chmod +x %t.dir/ocloc +// RUN: env PATH=%t.dir %clang -### -fsycl --no-offload-new-driver \ +// RUN: -fsycl-targets=spir64_gen --ocloc-path=/my/ocloc/dir %s 2>&1 \ +// RUN: | FileCheck -check-prefix=CHK-OCLOC-PATH-OLD %s + +/// Check that the 'exe' name is used for windows. +// RUN: %clang_cl -### -fsycl --no-offload-new-driver \ +// RUN: -fsycl-targets=spir64_gen --ocloc-path=/my/ocloc/dir -- %s 2>&1 \ +// RUN: | FileCheck -check-prefix=CHK-OCLOC-PATH-OLD-WIN %s +// RUN: %clang -### -target x86_64-pc-windows-msvc -fsycl \ +// RUN: --no-offload-new-driver -fsycl-targets=spir64_gen \ +// RUN: --ocloc-path=/my/ocloc/dir %s 2>&1 \ +// RUN: | FileCheck -check-prefix=CHK-OCLOC-PATH-OLD-WIN %s +// CHK-OCLOC-PATH-OLD-WIN: "/my/ocloc/dir{{[/\\]+}}ocloc.exe" "-output" + +/// Check that --ocloc-path= is forwarded to the clang-linker-wrapper for the +/// new offloading model. +// RUN: %clang -### -fsycl --offload-new-driver -fsycl-targets=spir64_gen \ +// RUN: --sysroot=%S/Inputs/SYCL --ocloc-path=/my/ocloc/dir %s 2>&1 \ +// RUN: | FileCheck -check-prefix=CHK-OCLOC-PATH-NEW %s +// RUN: %clang -### -fsycl --offload-new-driver \ +// RUN: -fsycl-targets=intel_gpu_pvc --sysroot=%S/Inputs/SYCL \ +// RUN: --ocloc-path=/my/ocloc/dir %s 2>&1 \ +// RUN: | FileCheck -check-prefix=CHK-OCLOC-PATH-NEW %s +// CHK-OCLOC-PATH-NEW: clang-linker-wrapper{{.*}} "--ocloc-path=/my/ocloc/dir" + +/// Check that --ocloc-path= is forwarded to the clang-sycl-linker. +// RUN: touch %t.bc +// RUN: %clangxx -### --target=spirv64 --sycl-link \ +// RUN: --ocloc-path=/my/ocloc/dir %t.bc 2>&1 \ +// RUN: | FileCheck -check-prefix=CHK-OCLOC-PATH-SYCL-LINK %s +// CHK-OCLOC-PATH-SYCL-LINK: clang-sycl-linker{{.*}} "--ocloc-path=/my/ocloc/dir" + +/// Check that --ocloc-path= is used when emitting the ocloc help information. +// RUN: %clang -### -fsycl -fsycl-help=gen --ocloc-path=/my/ocloc/dir %s 2>&1 \ +// RUN: | FileCheck -check-prefix=CHK-OCLOC-PATH-HELP %s +// CHK-OCLOC-PATH-HELP: Emitting help information for ocloc +// CHK-OCLOC-PATH-HELP: "/my/ocloc/dir{{[/\\]+}}ocloc" "--help" + +/// Check that --ocloc-path= does not warn as unused when no AOT compilation +/// for Intel GPU is being performed. +// RUN: %clang -### -fsycl -fsycl-targets=spir64 --ocloc-path=/my/ocloc/dir \ +// RUN: --sysroot=%S/Inputs/SYCL %s 2>&1 \ +// RUN: | FileCheck -check-prefix=CHK-OCLOC-PATH-UNUSED %s +// CHK-OCLOC-PATH-UNUSED-NOT: warning: argument unused during compilation diff --git a/clang/test/OffloadTools/clang-sycl-linker/basic.ll b/clang/test/OffloadTools/clang-sycl-linker/basic.ll index 448a166e0a5e1..5f2677cb11768 100644 --- a/clang/test/OffloadTools/clang-sycl-linker/basic.ll +++ b/clang/test/OffloadTools/clang-sycl-linker/basic.ll @@ -118,6 +118,19 @@ ; AOT-INTEL-GPU-NEXT: sycl-bundle: image kind: o, triple: spirv64, arch: bmg_g21 ; AOT-INTEL-GPU-NOT: {{.+}} ; +; Test that --ocloc-path= provides the location of the ocloc tool. +; RUN: clang-sycl-linker --dry-run -v --module-split-mode=link_unit -arch=bmg_g21 %t/input1.bc %t/input2.bc -o %t/aot-gpu.out 2>&1 \ +; RUN: --ocloc-path=/my/ocloc/dir \ +; RUN: | FileCheck %s --check-prefix=AOT-OCLOC-PATH +; AOT-OCLOC-PATH: "/my/ocloc/dir{{[/\\]+}}ocloc" {{.*}}-device bmg_g21 +; +; Test the diagnostic emitted when ocloc cannot be found in the given +; directory. +; RUN: not clang-sycl-linker -v --module-split-mode=link_unit -arch=bmg_g21 %t/input1.bc %t/input2.bc -o %t/aot-gpu.out 2>&1 \ +; RUN: --ocloc-path=%t/no-ocloc-here \ +; RUN: | FileCheck %s --check-prefix=AOT-OCLOC-PATH-ERR +; AOT-OCLOC-PATH-ERR: unable to find 'ocloc' in '{{.*}}no-ocloc-here' +; ; Test AOT compilation for an Intel CPU. ; Test that IMG_Object image kind is set for AOT compilation (Intel CPU). ; RUN: clang-sycl-linker --dry-run -v --module-split-mode=link_unit -arch=graniterapids %t/input1.bc %t/input2.bc -o %t/aot-cpu.out 2>&1 \ diff --git a/clang/tools/clang-linker-wrapper/ClangLinkerWrapper.cpp b/clang/tools/clang-linker-wrapper/ClangLinkerWrapper.cpp index 1b31f978d087f..714e8bc7510b5 100644 --- a/clang/tools/clang-linker-wrapper/ClangLinkerWrapper.cpp +++ b/clang/tools/clang-linker-wrapper/ClangLinkerWrapper.cpp @@ -338,6 +338,27 @@ Expected findProgram(StringRef Name, ArrayRef Paths) { return *Path; } +/// Locate the 'ocloc' tool used for Intel GPU AOT compilation. A user +/// provided --ocloc-path= is honored above any 'ocloc' that is visible via the +/// executable directory or the PATH environment variable. +Expected findOclocProgram(const ArgList &Args) { + if (Arg *A = Args.getLastArg(OPT_ocloc_path_EQ)) { + StringRef Dir = A->getValue(); + // Only look in the given directory. The tool name is resolved by + // findProgramByName, which takes care of any platform specific executable + // extension. + if (ErrorOr Path = sys::findProgramByName("ocloc", {Dir})) + return *Path; + if (DryRun) { + SmallString<128> OclocPath(Dir); + sys::path::append(OclocPath, "ocloc"); + return std::string(OclocPath); + } + return createStringError("Unable to find 'ocloc' in '" + Dir + "'"); + } + return findProgram("ocloc", {getExecutableDir("ocloc")}); +} + bool linkerSupportsLTO(const ArgList &Args) { llvm::Triple Triple(Args.getLastArgValue(OPT_triple_EQ)); return Triple.isNVPTX() || Triple.isAMDGPU() || @@ -1081,8 +1102,7 @@ runAOTCompileIntelGPU(StringRef InputFile, const ArgList &Args, const llvm::Triple Triple(Args.getLastArgValue(OPT_triple_EQ)); StringRef Arch(Args.getLastArgValue(OPT_arch_EQ)); SmallVector CmdArgs; - Expected OclocPath = - findProgram("ocloc", {getExecutableDir("ocloc")}); + Expected OclocPath = findOclocProgram(Args); if (!OclocPath) return OclocPath.takeError(); diff --git a/clang/tools/clang-linker-wrapper/LinkerWrapperOpts.td b/clang/tools/clang-linker-wrapper/LinkerWrapperOpts.td index a9b0ec46acdf8..115ebd9c11af5 100644 --- a/clang/tools/clang-linker-wrapper/LinkerWrapperOpts.td +++ b/clang/tools/clang-linker-wrapper/LinkerWrapperOpts.td @@ -16,6 +16,10 @@ def linker_path_EQ : Joined<["--"], "linker-path=">, def cuda_path_EQ : Joined<["--"], "cuda-path=">, Flags<[WrapperOnlyOption]>, MetaVarName<"">, HelpText<"Set the system CUDA path">; +def ocloc_path_EQ : Joined<["--"], "ocloc-path=">, + Flags<[WrapperOnlyOption]>, MetaVarName<"">, + HelpText<"Directory containing the ocloc tool used for Intel GPU AOT " + "compilation">; def host_triple_EQ : Joined<["--"], "host-triple=">, Flags<[WrapperOnlyOption]>, MetaVarName<"">, diff --git a/clang/tools/clang-sycl-linker/ClangSYCLLinker.cpp b/clang/tools/clang-sycl-linker/ClangSYCLLinker.cpp index 5e937507289ec..071487420b852 100644 --- a/clang/tools/clang-sycl-linker/ClangSYCLLinker.cpp +++ b/clang/tools/clang-sycl-linker/ClangSYCLLinker.cpp @@ -169,6 +169,25 @@ static Expected findProgram(const ArgList &Args, StringRef Name, return *Path; } +/// Locate the 'ocloc' tool used for Intel GPU AOT compilation. +static Expected findOcloc(const ArgList &Args) { + if (Arg *A = Args.getLastArg(OPT_ocloc_path_EQ)) { + StringRef Dir = A->getValue(); + if (DryRun) { + SmallString<128> OclocPath(Dir); + sys::path::append(OclocPath, "ocloc"); + return std::string(OclocPath); + } + // Only look in the given directory. The tool name is resolved by + // findProgramByName, which takes care of any platform specific executable + // extension. + if (ErrorOr Path = sys::findProgramByName("ocloc", {Dir})) + return *Path; + return createStringError("unable to find 'ocloc' in '" + Dir + "'"); + } + return findProgram(Args, "ocloc", {getMainExecutable("ocloc")}); +} + static void printCommands(ArrayRef CmdArgs) { if (CmdArgs.empty()) return; @@ -710,8 +729,7 @@ static Error runAOTCompileIntelCPU(StringRef InputFile, StringRef OutputFile, static Error runAOTCompileIntelGPU(StringRef InputFile, StringRef OutputFile, const ArgList &Args) { SmallVector CmdArgs; - Expected OclocPath = - findProgram(Args, "ocloc", {getMainExecutable("ocloc")}); + Expected OclocPath = findOcloc(Args); if (!OclocPath) return OclocPath.takeError(); diff --git a/clang/tools/clang-sycl-linker/SYCLLinkOpts.td b/clang/tools/clang-sycl-linker/SYCLLinkOpts.td index 965ab6b5e4e65..e428757ceab81 100644 --- a/clang/tools/clang-sycl-linker/SYCLLinkOpts.td +++ b/clang/tools/clang-sycl-linker/SYCLLinkOpts.td @@ -69,6 +69,11 @@ def ocloc_options_EQ : Joined<["--", "-"], "ocloc-options=">, Flags<[LinkerOnlyOption]>, HelpText<"Options passed to ocloc for Intel GPU AOT compilation">; +def ocloc_path_EQ : Joined<["--", "-"], "ocloc-path=">, + Flags<[LinkerOnlyOption]>, MetaVarName<"">, + HelpText<"Directory containing the ocloc tool used for Intel GPU AOT " + "compilation. Takes precedence over any ocloc found in PATH">; + def opencl_aot_options_EQ : Joined<["--", "-"], "opencl-aot-options=">, Flags<[LinkerOnlyOption]>, HelpText<"Options passed to opencl-aot for Intel CPU AOT compilation">; From d3b9cfe2fa5f00240c7d85078f4ee5f14f90ca4d Mon Sep 17 00:00:00 2001 From: Michael D Toguchi Date: Mon, 10 Aug 2026 12:45:48 -0700 Subject: [PATCH 03/11] Update func name --- clang/tools/clang-linker-wrapper/ClangLinkerWrapper.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/clang/tools/clang-linker-wrapper/ClangLinkerWrapper.cpp b/clang/tools/clang-linker-wrapper/ClangLinkerWrapper.cpp index 6bc603005ed8f..d76bf735412c7 100644 --- a/clang/tools/clang-linker-wrapper/ClangLinkerWrapper.cpp +++ b/clang/tools/clang-linker-wrapper/ClangLinkerWrapper.cpp @@ -341,7 +341,7 @@ Expected findProgram(StringRef Name, ArrayRef Paths) { /// Locate the 'ocloc' tool used for Intel GPU AOT compilation. A user /// provided --ocloc-path= is honored above any 'ocloc' that is visible via the /// executable directory or the PATH environment variable. -Expected findOclocProgram(const ArgList &Args) { +Expected findOcloc(const ArgList &Args) { if (Arg *A = Args.getLastArg(OPT_ocloc_path_EQ)) { StringRef Dir = A->getValue(); // Only look in the given directory. The tool name is resolved by @@ -1102,7 +1102,7 @@ runAOTCompileIntelGPU(StringRef InputFile, const ArgList &Args, const llvm::Triple Triple(Args.getLastArgValue(OPT_triple_EQ)); StringRef Arch(Args.getLastArgValue(OPT_arch_EQ)); SmallVector CmdArgs; - Expected OclocPath = findOclocProgram(Args); + Expected OclocPath = findOcloc(Args); if (!OclocPath) return OclocPath.takeError(); From a469b825d9366411eb2205113c6d7472e80e2de3 Mon Sep 17 00:00:00 2001 From: Michael D Toguchi Date: Mon, 10 Aug 2026 14:53:11 -0700 Subject: [PATCH 04/11] Improve windows testing --- clang/test/Driver/sycl-ocloc-path.cpp | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/clang/test/Driver/sycl-ocloc-path.cpp b/clang/test/Driver/sycl-ocloc-path.cpp index 007e97aa23b1c..5c99a816ebea2 100644 --- a/clang/test/Driver/sycl-ocloc-path.cpp +++ b/clang/test/Driver/sycl-ocloc-path.cpp @@ -12,15 +12,17 @@ // RUN: %clang -### -fsycl --no-offload-new-driver \ // RUN: -fsycl-targets=intel_gpu_pvc --ocloc-path=/my/ocloc/dir %s 2>&1 \ // RUN: | FileCheck -check-prefix=CHK-OCLOC-PATH-OLD %s -// CHK-OCLOC-PATH-OLD: "/my/ocloc/dir{{[/\\]+}}ocloc" "-output" +// CHK-OCLOC-PATH-OLD: "/my/ocloc/dir{{[/\\]+}}ocloc{{(\.exe)?}}" "-output" /// Check that the user provided location wins over an ocloc that is visible -/// via the PATH. -// RUN: mkdir -p %t.dir -// RUN: touch %t.dir/ocloc -// RUN: chmod +x %t.dir/ocloc -// RUN: env PATH=%t.dir %clang -### -fsycl --no-offload-new-driver \ -// RUN: -fsycl-targets=spir64_gen --ocloc-path=/my/ocloc/dir %s 2>&1 \ +/// via the PATH. The fake ocloc must be findable, which means it needs the +/// execute bit set on linux and the executable extension on windows. +// RUN: rm -rf %t.dir && mkdir -p %t.dir +// RUN: %if system-windows %{ touch %t.dir/ocloc.exe %} \ +// RUN: %else %{ touch %t.dir/ocloc && chmod +x %t.dir/ocloc %} +// RUN: env "PATH=%t.dir%{pathsep}%PATH%" %clang -### -fsycl \ +// RUN: --no-offload-new-driver -fsycl-targets=spir64_gen \ +// RUN: --ocloc-path=/my/ocloc/dir %s 2>&1 \ // RUN: | FileCheck -check-prefix=CHK-OCLOC-PATH-OLD %s /// Check that the 'exe' name is used for windows. From b4c1da62d636659468b38f155456112bcb803935 Mon Sep 17 00:00:00 2001 From: Michael D Toguchi Date: Wed, 12 Aug 2026 14:36:57 -0700 Subject: [PATCH 05/11] Sync up comments and help text --- clang/include/clang/Options/Options.td | 4 ++-- clang/lib/Driver/ToolChains/Clang.cpp | 2 +- clang/lib/Driver/ToolChains/SPIRV.cpp | 3 +-- clang/lib/Driver/ToolChains/SYCL.h | 1 + clang/tools/clang-linker-wrapper/ClangLinkerWrapper.cpp | 4 +--- clang/tools/clang-linker-wrapper/LinkerWrapperOpts.td | 4 ++-- clang/tools/clang-sycl-linker/SYCLLinkOpts.td | 4 ++-- 7 files changed, 10 insertions(+), 12 deletions(-) diff --git a/clang/include/clang/Options/Options.td b/clang/include/clang/Options/Options.td index ac8179df76054..5f1385acec2fc 100644 --- a/clang/include/clang/Options/Options.td +++ b/clang/include/clang/Options/Options.td @@ -7906,8 +7906,8 @@ def fsycl_help : Flag<["-"], "fsycl-help">, Alias, def ocloc_path_EQ : Joined<["--"], "ocloc-path=">, Visibility<[ClangOption, CLOption]>, Flags<[NoXarchOption, NoArgumentUnused]>, MetaVarName<"">, - HelpText<"Directory containing the ocloc tool, which is used for ahead of " - "time compilation targeting Intel GPUs.">; + HelpText<"Path to the ocloc tool, which is used for ahead of time " + "compilation targeting Intel GPUs">; def fsycl_libspirv_path_EQ : Joined<["-"], "fsycl-libspirv-path=">, HelpText<"Path to libspirv library">; def fno_sycl_libspirv : Flag<["-"], "fno-sycl-libspirv">, diff --git a/clang/lib/Driver/ToolChains/Clang.cpp b/clang/lib/Driver/ToolChains/Clang.cpp index b3b2a0a46ebef..a9648b250aecf 100644 --- a/clang/lib/Driver/ToolChains/Clang.cpp +++ b/clang/lib/Driver/ToolChains/Clang.cpp @@ -12132,7 +12132,7 @@ void LinkerWrapper::ConstructJob(Compilation &C, const JobAction &JA, // Add any SYCL offloading specific options to the clang-linker-wrapper if (C.hasOffloadToolChain()) { - // Forward the user provided location along to the tools that call ocloc. + // Forward the user provided location for ocloc. if (Arg *A = Args.getLastArg(options::OPT_ocloc_path_EQ)) CmdArgs.push_back( Args.MakeArgString(Twine("--ocloc-path=") + A->getValue())); diff --git a/clang/lib/Driver/ToolChains/SPIRV.cpp b/clang/lib/Driver/ToolChains/SPIRV.cpp index 4f577e367e820..e132ff8a1a7d3 100644 --- a/clang/lib/Driver/ToolChains/SPIRV.cpp +++ b/clang/lib/Driver/ToolChains/SPIRV.cpp @@ -186,8 +186,7 @@ void SPIRV::Linker::ConstructJob(Compilation &C, const JobAction &JA, Linker = ToolChain.GetProgramPath("clang-sycl-linker"); if (Args.hasArg(options::OPT_v)) CmdArgs.push_back("-v"); - // ocloc is acquired externally, so it is not guaranteed to be in the PATH. - // Forward the user provided location along to the AOT compilation step. + // Forward the user provided location to ocloc. if (Arg *A = Args.getLastArg(options::OPT_ocloc_path_EQ)) CmdArgs.push_back( Args.MakeArgString(Twine("--ocloc-path=") + A->getValue())); diff --git a/clang/lib/Driver/ToolChains/SYCL.h b/clang/lib/Driver/ToolChains/SYCL.h index 312c345c645ae..513bd3ce37cec 100644 --- a/clang/lib/Driver/ToolChains/SYCL.h +++ b/clang/lib/Driver/ToolChains/SYCL.h @@ -84,6 +84,7 @@ StringRef getGenGRFFlag(StringRef GRFMode); // Returns the full path of the ocloc tool to be used for AOT compilation. A // user provided --ocloc-path= is honored above all other lookup locations. +// If not found, the tool (ocloc) is returned with no directory. const char *getOclocPath(Compilation &C, const ToolChain &TC, const llvm::opt::ArgList &Args); diff --git a/clang/tools/clang-linker-wrapper/ClangLinkerWrapper.cpp b/clang/tools/clang-linker-wrapper/ClangLinkerWrapper.cpp index d76bf735412c7..4e0b3c76ca638 100644 --- a/clang/tools/clang-linker-wrapper/ClangLinkerWrapper.cpp +++ b/clang/tools/clang-linker-wrapper/ClangLinkerWrapper.cpp @@ -338,9 +338,7 @@ Expected findProgram(StringRef Name, ArrayRef Paths) { return *Path; } -/// Locate the 'ocloc' tool used for Intel GPU AOT compilation. A user -/// provided --ocloc-path= is honored above any 'ocloc' that is visible via the -/// executable directory or the PATH environment variable. +/// Locate the 'ocloc' tool used for Intel GPU AOT compilation. Expected findOcloc(const ArgList &Args) { if (Arg *A = Args.getLastArg(OPT_ocloc_path_EQ)) { StringRef Dir = A->getValue(); diff --git a/clang/tools/clang-linker-wrapper/LinkerWrapperOpts.td b/clang/tools/clang-linker-wrapper/LinkerWrapperOpts.td index 115ebd9c11af5..e0321aec4bed9 100644 --- a/clang/tools/clang-linker-wrapper/LinkerWrapperOpts.td +++ b/clang/tools/clang-linker-wrapper/LinkerWrapperOpts.td @@ -18,8 +18,8 @@ def cuda_path_EQ : Joined<["--"], "cuda-path=">, HelpText<"Set the system CUDA path">; def ocloc_path_EQ : Joined<["--"], "ocloc-path=">, Flags<[WrapperOnlyOption]>, MetaVarName<"">, - HelpText<"Directory containing the ocloc tool used for Intel GPU AOT " - "compilation">; + HelpText<""Path to the ocloc tool, which is used for ahead of time " ++ "compilation targeting Intel GPUs">; def host_triple_EQ : Joined<["--"], "host-triple=">, Flags<[WrapperOnlyOption]>, MetaVarName<"">, diff --git a/clang/tools/clang-sycl-linker/SYCLLinkOpts.td b/clang/tools/clang-sycl-linker/SYCLLinkOpts.td index e428757ceab81..3f608a07447ca 100644 --- a/clang/tools/clang-sycl-linker/SYCLLinkOpts.td +++ b/clang/tools/clang-sycl-linker/SYCLLinkOpts.td @@ -71,8 +71,8 @@ def ocloc_options_EQ : Joined<["--", "-"], "ocloc-options=">, def ocloc_path_EQ : Joined<["--", "-"], "ocloc-path=">, Flags<[LinkerOnlyOption]>, MetaVarName<"">, - HelpText<"Directory containing the ocloc tool used for Intel GPU AOT " - "compilation. Takes precedence over any ocloc found in PATH">; + HelpText<""Path to the ocloc tool, which is used for ahead of time " ++ "compilation targeting Intel GPUs">; def opencl_aot_options_EQ : Joined<["--", "-"], "opencl-aot-options=">, Flags<[LinkerOnlyOption]>, From 7a70b18c48875b45634b3078f3b43d039015ead1 Mon Sep 17 00:00:00 2001 From: Michael D Toguchi Date: Wed, 12 Aug 2026 14:59:14 -0700 Subject: [PATCH 06/11] Fix quotes --- clang/tools/clang-linker-wrapper/LinkerWrapperOpts.td | 4 ++-- clang/tools/clang-sycl-linker/SYCLLinkOpts.td | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/clang/tools/clang-linker-wrapper/LinkerWrapperOpts.td b/clang/tools/clang-linker-wrapper/LinkerWrapperOpts.td index e0321aec4bed9..dcbdecfe1fa81 100644 --- a/clang/tools/clang-linker-wrapper/LinkerWrapperOpts.td +++ b/clang/tools/clang-linker-wrapper/LinkerWrapperOpts.td @@ -18,8 +18,8 @@ def cuda_path_EQ : Joined<["--"], "cuda-path=">, HelpText<"Set the system CUDA path">; def ocloc_path_EQ : Joined<["--"], "ocloc-path=">, Flags<[WrapperOnlyOption]>, MetaVarName<"">, - HelpText<""Path to the ocloc tool, which is used for ahead of time " -+ "compilation targeting Intel GPUs">; + HelpText<"Path to the ocloc tool, which is used for ahead of time " ++ "compilation targeting Intel GPUs">; def host_triple_EQ : Joined<["--"], "host-triple=">, Flags<[WrapperOnlyOption]>, MetaVarName<"">, diff --git a/clang/tools/clang-sycl-linker/SYCLLinkOpts.td b/clang/tools/clang-sycl-linker/SYCLLinkOpts.td index 3f608a07447ca..b61a84018f641 100644 --- a/clang/tools/clang-sycl-linker/SYCLLinkOpts.td +++ b/clang/tools/clang-sycl-linker/SYCLLinkOpts.td @@ -71,8 +71,8 @@ def ocloc_options_EQ : Joined<["--", "-"], "ocloc-options=">, def ocloc_path_EQ : Joined<["--", "-"], "ocloc-path=">, Flags<[LinkerOnlyOption]>, MetaVarName<"">, - HelpText<""Path to the ocloc tool, which is used for ahead of time " -+ "compilation targeting Intel GPUs">; + HelpText<"Path to the ocloc tool, which is used for ahead of time " ++ "compilation targeting Intel GPUs">; def opencl_aot_options_EQ : Joined<["--", "-"], "opencl-aot-options=">, Flags<[LinkerOnlyOption]>, From 03c8be43a83402cc8b286727a3de5c70c8d8910d Mon Sep 17 00:00:00 2001 From: Michael D Toguchi Date: Wed, 12 Aug 2026 15:11:34 -0700 Subject: [PATCH 07/11] extraneous character --- clang/tools/clang-linker-wrapper/LinkerWrapperOpts.td | 2 +- clang/tools/clang-sycl-linker/SYCLLinkOpts.td | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/clang/tools/clang-linker-wrapper/LinkerWrapperOpts.td b/clang/tools/clang-linker-wrapper/LinkerWrapperOpts.td index dcbdecfe1fa81..3b78f1fe125dc 100644 --- a/clang/tools/clang-linker-wrapper/LinkerWrapperOpts.td +++ b/clang/tools/clang-linker-wrapper/LinkerWrapperOpts.td @@ -19,7 +19,7 @@ def cuda_path_EQ : Joined<["--"], "cuda-path=">, def ocloc_path_EQ : Joined<["--"], "ocloc-path=">, Flags<[WrapperOnlyOption]>, MetaVarName<"">, HelpText<"Path to the ocloc tool, which is used for ahead of time " -+ "compilation targeting Intel GPUs">; + "compilation targeting Intel GPUs">; def host_triple_EQ : Joined<["--"], "host-triple=">, Flags<[WrapperOnlyOption]>, MetaVarName<"">, diff --git a/clang/tools/clang-sycl-linker/SYCLLinkOpts.td b/clang/tools/clang-sycl-linker/SYCLLinkOpts.td index b61a84018f641..ea21381486040 100644 --- a/clang/tools/clang-sycl-linker/SYCLLinkOpts.td +++ b/clang/tools/clang-sycl-linker/SYCLLinkOpts.td @@ -72,7 +72,7 @@ def ocloc_options_EQ : Joined<["--", "-"], "ocloc-options=">, def ocloc_path_EQ : Joined<["--", "-"], "ocloc-path=">, Flags<[LinkerOnlyOption]>, MetaVarName<"">, HelpText<"Path to the ocloc tool, which is used for ahead of time " -+ "compilation targeting Intel GPUs">; + "compilation targeting Intel GPUs">; def opencl_aot_options_EQ : Joined<["--", "-"], "opencl-aot-options=">, Flags<[LinkerOnlyOption]>, From fff04f46aeffa6ca51d7d70fff4b979d69b12c85 Mon Sep 17 00:00:00 2001 From: Michael D Toguchi Date: Thu, 13 Aug 2026 12:06:59 -0700 Subject: [PATCH 08/11] Update --ocloc-path= usage behaviors Fixes a few items from review - Improved error handling with -fsycl-help=gen - Make getOclocPath a static helper - make --ocloc-path= usage consistent when no value provided --- clang/lib/Driver/Driver.cpp | 19 +++++++++++++++++-- clang/lib/Driver/ToolChains/SYCL.cpp | 6 +++--- clang/lib/Driver/ToolChains/SYCL.h | 6 ------ clang/test/Driver/clang-linker-wrapper.cpp | 4 ++++ clang/test/Driver/sycl-ocloc-path.cpp | 18 ++++++++++++++++++ .../OffloadTools/clang-sycl-linker/basic.ll | 11 +++++++---- .../ClangLinkerWrapper.cpp | 3 +++ .../clang-sycl-linker/ClangSYCLLinker.cpp | 3 +++ 8 files changed, 55 insertions(+), 15 deletions(-) diff --git a/clang/lib/Driver/Driver.cpp b/clang/lib/Driver/Driver.cpp index e21bbfa70f9fc..d84fa44528cb2 100644 --- a/clang/lib/Driver/Driver.cpp +++ b/clang/lib/Driver/Driver.cpp @@ -316,6 +316,16 @@ InputArgList Driver::ParseArgStrings(ArrayRef ArgStrings, diag::warn_drv_empty_joined_argument, SourceLocation()) > DiagnosticsEngine::Warning; } + + // An empty --ocloc-path= is rejected here for consistent usage for areas + // that consume it. + if (A->getOption().matches(options::OPT_ocloc_path_EQ) && + A->containsValue("")) { + Diag(diag::err_drv_invalid_value) << A->getSpelling() << A->getValue(); + ContainsError |= Diags.getDiagnosticLevel(diag::err_drv_invalid_value, + SourceLocation()) > + DiagnosticsEngine::Warning; + } } for (const Arg *A : Args.filtered(options::OPT_UNKNOWN)) { @@ -2799,6 +2809,10 @@ void Driver::PrintHelp(bool ShowHidden) const { // Print the help from any of the given tools which are used for AOT // compilation for SYCL void Driver::PrintSYCLToolHelp(const Compilation &C) const { + // Do not run any external tools if the command line was already rejected. + if (C.containsError()) + return; + SmallVector, 4> HelpArgs; // Populate the vector with the tools and help options @@ -2847,12 +2861,13 @@ void Driver::PrintSYCLToolHelp(const Compilation &C) const { continue; } auto ToolBinary = llvm::sys::findProgramByName(ExecPath); - if (ToolBinary.getError()) { + if (ToolBinary.getError() || !llvm::sys::fs::can_execute(*ToolBinary)) { C.getDriver().Diag(diag::err_drv_command_failure) << ExecPath; continue; } // Run the Tool. - llvm::sys::ExecuteAndWait(ToolBinary.get(), ToolArgs); + if (llvm::sys::ExecuteAndWait(ToolBinary.get(), ToolArgs) < 0) + C.getDriver().Diag(diag::err_drv_command_failure) << ExecPath; } } diff --git a/clang/lib/Driver/ToolChains/SYCL.cpp b/clang/lib/Driver/ToolChains/SYCL.cpp index 840584225fc70..7c3c9e31c4ec2 100644 --- a/clang/lib/Driver/ToolChains/SYCL.cpp +++ b/clang/lib/Driver/ToolChains/SYCL.cpp @@ -1054,8 +1054,8 @@ static const char *makeExeName(Compilation &C, StringRef Name) { return C.getArgs().MakeArgString(ExeName); } -const char *SYCL::gen::getOclocPath(Compilation &C, const ToolChain &TC, - const llvm::opt::ArgList &Args) { +static const char *getOclocPath(Compilation &C, const ToolChain &TC, + const llvm::opt::ArgList &Args) { const char *ExeName = makeExeName(C, "ocloc"); // A user provided --ocloc-path= takes precedence over any ocloc that is // found via the program paths or the PATH environment variable. @@ -1130,7 +1130,7 @@ void SYCL::gen::BackendCompiler::ConstructJob(Compilation &C, Device); TC.TranslateLinkerTargetArgs(getToolChain().getTriple(), Args, CmdArgs, Device); - const char *Exec = SYCL::gen::getOclocPath(C, getToolChain(), Args); + const char *Exec = getOclocPath(C, getToolChain(), Args); auto Cmd = std::make_unique(JA, *this, ResponseFileSupport::None(), Exec, CmdArgs, ArrayRef{}); if (!ForeachInputs.empty()) { diff --git a/clang/lib/Driver/ToolChains/SYCL.h b/clang/lib/Driver/ToolChains/SYCL.h index 513bd3ce37cec..777c1193c260f 100644 --- a/clang/lib/Driver/ToolChains/SYCL.h +++ b/clang/lib/Driver/ToolChains/SYCL.h @@ -82,12 +82,6 @@ StringRef resolveGenDevice(StringRef DeviceName); SmallString<64> getGenDeviceMacro(StringRef DeviceName); StringRef getGenGRFFlag(StringRef GRFMode); -// Returns the full path of the ocloc tool to be used for AOT compilation. A -// user provided --ocloc-path= is honored above all other lookup locations. -// If not found, the tool (ocloc) is returned with no directory. -const char *getOclocPath(Compilation &C, const ToolChain &TC, - const llvm::opt::ArgList &Args); - // Prefix for GPU specific targets used for -fsycl-targets constexpr char IntelGPU[] = "intel_gpu_"; constexpr char NvidiaGPU[] = "nvidia_gpu_"; diff --git a/clang/test/Driver/clang-linker-wrapper.cpp b/clang/test/Driver/clang-linker-wrapper.cpp index c9dd6a4e285e9..47a1aaab29a35 100644 --- a/clang/test/Driver/clang-linker-wrapper.cpp +++ b/clang/test/Driver/clang-linker-wrapper.cpp @@ -149,6 +149,10 @@ // RUN: not clang-linker-wrapper --ocloc-path=%t.no-ocloc-here --linker-path=/usr/bin/ld -o /dev/null %t1.o 2>&1 | FileCheck -check-prefix=CHK-OCLOC-PATH-ERR %s // CHK-OCLOC-PATH-ERR: Unable to find 'ocloc' in '{{.*}}no-ocloc-here' +// Check that an empty --ocloc-path= is rejected. +// RUN: not clang-linker-wrapper --ocloc-path= --linker-path=/usr/bin/ld -o /dev/null %t1.o --dry-run 2>&1 | FileCheck -check-prefix=CHK-OCLOC-PATH-NOARG %s +// CHK-OCLOC-PATH-NOARG: no directory given for '--ocloc-path=' + /// Check for list of commands for standalone clang-linker-wrapper run for sycl (AOT for Intel CPU) // ------- // Generate .o file as linker wrapper input. diff --git a/clang/test/Driver/sycl-ocloc-path.cpp b/clang/test/Driver/sycl-ocloc-path.cpp index 5c99a816ebea2..63210454f324f 100644 --- a/clang/test/Driver/sycl-ocloc-path.cpp +++ b/clang/test/Driver/sycl-ocloc-path.cpp @@ -59,6 +59,24 @@ // CHK-OCLOC-PATH-HELP: Emitting help information for ocloc // CHK-OCLOC-PATH-HELP: "/my/ocloc/dir{{[/\\]+}}ocloc" "--help" +/// Check the diagnostic emitted when the given directory does not contain a +/// usable ocloc. Without -### the tool is actually launched, so the composed +/// path is expected to be diagnosed instead of being silently ignored. +// RUN: rm -rf %t.empty.dir && mkdir -p %t.empty.dir +// RUN: not %clang -fsycl -fsycl-help=gen --ocloc-path=%t.empty.dir %s 2>&1 \ +// RUN: | FileCheck -check-prefix=CHK-OCLOC-PATH-HELP-ERR %s +// CHK-OCLOC-PATH-HELP-ERR: error: unable to execute command: {{.*}}ocloc + +/// Check that an empty --ocloc-path= is rejected instead of falling back to +/// another ocloc. +// RUN: not %clang -### -fsycl --no-offload-new-driver \ +// RUN: -fsycl-targets=spir64_gen --ocloc-path= %s 2>&1 \ +// RUN: | FileCheck -check-prefix=CHK-OCLOC-PATH-EMPTY %s +// RUN: not %clang -fsycl -fsycl-help=gen --ocloc-path= %s 2>&1 \ +// RUN: | FileCheck -check-prefix=CHK-OCLOC-PATH-EMPTY %s +// CHK-OCLOC-PATH-EMPTY: error: invalid value '' in '--ocloc-path=' +// CHK-OCLOC-PATH-EMPTY-NOT: Emitting help information + /// Check that --ocloc-path= does not warn as unused when no AOT compilation /// for Intel GPU is being performed. // RUN: %clang -### -fsycl -fsycl-targets=spir64 --ocloc-path=/my/ocloc/dir \ diff --git a/clang/test/OffloadTools/clang-sycl-linker/basic.ll b/clang/test/OffloadTools/clang-sycl-linker/basic.ll index 5f2677cb11768..ef8310f0167bb 100644 --- a/clang/test/OffloadTools/clang-sycl-linker/basic.ll +++ b/clang/test/OffloadTools/clang-sycl-linker/basic.ll @@ -119,18 +119,21 @@ ; AOT-INTEL-GPU-NOT: {{.+}} ; ; Test that --ocloc-path= provides the location of the ocloc tool. -; RUN: clang-sycl-linker --dry-run -v --module-split-mode=link_unit -arch=bmg_g21 %t/input1.bc %t/input2.bc -o %t/aot-gpu.out 2>&1 \ -; RUN: --ocloc-path=/my/ocloc/dir \ +; RUN: clang-sycl-linker --dry-run -v --module-split-mode=link_unit -arch=bmg_g21 %t/input1.bc %t/input2.bc -o %t/aot-gpu.out --ocloc-path=/my/ocloc/dir 2>&1 \ ; RUN: | FileCheck %s --check-prefix=AOT-OCLOC-PATH ; AOT-OCLOC-PATH: "/my/ocloc/dir{{[/\\]+}}ocloc" {{.*}}-device bmg_g21 ; ; Test the diagnostic emitted when ocloc cannot be found in the given ; directory. -; RUN: not clang-sycl-linker -v --module-split-mode=link_unit -arch=bmg_g21 %t/input1.bc %t/input2.bc -o %t/aot-gpu.out 2>&1 \ -; RUN: --ocloc-path=%t/no-ocloc-here \ +; RUN: not clang-sycl-linker -v --module-split-mode=link_unit -arch=bmg_g21 %t/input1.bc %t/input2.bc -o %t/aot-gpu.out --ocloc-path=%t/no-ocloc-here 2>&1 \ ; RUN: | FileCheck %s --check-prefix=AOT-OCLOC-PATH-ERR ; AOT-OCLOC-PATH-ERR: unable to find 'ocloc' in '{{.*}}no-ocloc-here' ; +; Test that an empty --ocloc-path= is rejected. +; RUN: not clang-sycl-linker --dry-run -v -arch=bmg_g21 %t/input1.bc --ocloc-path= -o %t/aot-gpu.out 2>&1 \ +; RUN: | FileCheck %s --check-prefix=AOT-OCLOC-PATH-NOARG +; AOT-OCLOC-PATH-NOARG: no directory given for '--ocloc-path=' +; ; Test AOT compilation for an Intel CPU. ; Test that IMG_Object image kind is set for AOT compilation (Intel CPU). ; RUN: clang-sycl-linker --dry-run -v --module-split-mode=link_unit -arch=graniterapids %t/input1.bc %t/input2.bc -o %t/aot-cpu.out 2>&1 \ diff --git a/clang/tools/clang-linker-wrapper/ClangLinkerWrapper.cpp b/clang/tools/clang-linker-wrapper/ClangLinkerWrapper.cpp index 4e0b3c76ca638..ae576ab05aeaa 100644 --- a/clang/tools/clang-linker-wrapper/ClangLinkerWrapper.cpp +++ b/clang/tools/clang-linker-wrapper/ClangLinkerWrapper.cpp @@ -342,6 +342,9 @@ Expected findProgram(StringRef Name, ArrayRef Paths) { Expected findOcloc(const ArgList &Args) { if (Arg *A = Args.getLastArg(OPT_ocloc_path_EQ)) { StringRef Dir = A->getValue(); + if (Dir.empty()) + return createStringError("no directory given for '" + A->getSpelling() + + "'"); // Only look in the given directory. The tool name is resolved by // findProgramByName, which takes care of any platform specific executable // extension. diff --git a/clang/tools/clang-sycl-linker/ClangSYCLLinker.cpp b/clang/tools/clang-sycl-linker/ClangSYCLLinker.cpp index 071487420b852..0341c35497a26 100644 --- a/clang/tools/clang-sycl-linker/ClangSYCLLinker.cpp +++ b/clang/tools/clang-sycl-linker/ClangSYCLLinker.cpp @@ -173,6 +173,9 @@ static Expected findProgram(const ArgList &Args, StringRef Name, static Expected findOcloc(const ArgList &Args) { if (Arg *A = Args.getLastArg(OPT_ocloc_path_EQ)) { StringRef Dir = A->getValue(); + if (Dir.empty()) + return createStringError("no directory given for '" + A->getSpelling() + + "'"); if (DryRun) { SmallString<128> OclocPath(Dir); sys::path::append(OclocPath, "ocloc"); From 5e0620aa398c719076e01110cb27903e888eef9d Mon Sep 17 00:00:00 2001 From: Michael D Toguchi Date: Thu, 13 Aug 2026 15:18:15 -0700 Subject: [PATCH 09/11] Use common func for grabbing ocloc location --- clang/lib/Driver/Driver.cpp | 23 ++++++++++++----------- clang/lib/Driver/ToolChains/SYCL.cpp | 8 ++++---- clang/lib/Driver/ToolChains/SYCL.h | 7 +++++++ clang/test/Driver/sycl-ocloc-path.cpp | 9 ++++++++- 4 files changed, 31 insertions(+), 16 deletions(-) diff --git a/clang/lib/Driver/Driver.cpp b/clang/lib/Driver/Driver.cpp index d84fa44528cb2..f9e4749fdc4f9 100644 --- a/clang/lib/Driver/Driver.cpp +++ b/clang/lib/Driver/Driver.cpp @@ -2837,21 +2837,22 @@ void Driver::PrintSYCLToolHelp(const Compilation &C) const { // Go through the args and emit the help information for each. for (auto &HA : HelpArgs) { - llvm::outs() << "Emitting help information for " << std::get<1>(HA) << '\n' - << "Use triple of '" << std::get<0>(HA).normalize() << - "' to enable ahead of time compilation\n"; + StringRef ToolName = std::get<1>(HA); + llvm::outs() << "Emitting help information for " << ToolName << '\n' + << "Use triple of '" << std::get<0>(HA).normalize() + << "' to enable ahead of time compilation\n"; // Flush out the buffer before calling the external tool. llvm::outs().flush(); - std::vector ToolArgs = {std::get<1>(HA), std::get<2>(HA), + std::vector ToolArgs = {ToolName, std::get<2>(HA), std::get<3>(HA)}; SmallString<128> ExecPath; - // A user provided --ocloc-path= overrides the usual tool lookup for ocloc. - if (Arg *A = C.getArgs().getLastArg(options::OPT_ocloc_path_EQ); - A && std::get<1>(HA) == "ocloc") { - ExecPath = A->getValue(); - llvm::sys::path::append(ExecPath, std::get<1>(HA)); - } else - ExecPath = C.getDefaultToolChain().GetProgramPath(std::get<1>(HA).data()); + // The lookup for ocloc is shared with the AOT compilation step, which + // honors any user provided --ocloc-path=. + if (ToolName == "ocloc") + ExecPath = tools::SYCL::gen::getOclocPath(C, C.getDefaultToolChain(), + C.getArgs()); + else + ExecPath = C.getDefaultToolChain().GetProgramPath(ToolName.data()); // do not run the tools with -###. if (C.getArgs().hasArg(options::OPT__HASH_HASH_HASH)) { llvm::errs() << "\"" << ExecPath << "\" \"" << ToolArgs[1] << "\""; diff --git a/clang/lib/Driver/ToolChains/SYCL.cpp b/clang/lib/Driver/ToolChains/SYCL.cpp index 7c3c9e31c4ec2..f1eda63c72435 100644 --- a/clang/lib/Driver/ToolChains/SYCL.cpp +++ b/clang/lib/Driver/ToolChains/SYCL.cpp @@ -1046,7 +1046,7 @@ void SYCL::Linker::ConstructJob(Compilation &C, const JobAction &JA, SpirvInputs); } -static const char *makeExeName(Compilation &C, StringRef Name) { +static const char *makeExeName(const Compilation &C, StringRef Name) { llvm::SmallString<8> ExeName(Name); const ToolChain *HostTC = C.getSingleOffloadToolChain(); if (HostTC->getTriple().isWindowsMSVCEnvironment()) @@ -1054,8 +1054,8 @@ static const char *makeExeName(Compilation &C, StringRef Name) { return C.getArgs().MakeArgString(ExeName); } -static const char *getOclocPath(Compilation &C, const ToolChain &TC, - const llvm::opt::ArgList &Args) { +const char *SYCL::gen::getOclocPath(const Compilation &C, const ToolChain &TC, + const llvm::opt::ArgList &Args) { const char *ExeName = makeExeName(C, "ocloc"); // A user provided --ocloc-path= takes precedence over any ocloc that is // found via the program paths or the PATH environment variable. @@ -1130,7 +1130,7 @@ void SYCL::gen::BackendCompiler::ConstructJob(Compilation &C, Device); TC.TranslateLinkerTargetArgs(getToolChain().getTriple(), Args, CmdArgs, Device); - const char *Exec = getOclocPath(C, getToolChain(), Args); + const char *Exec = SYCL::gen::getOclocPath(C, getToolChain(), Args); auto Cmd = std::make_unique(JA, *this, ResponseFileSupport::None(), Exec, CmdArgs, ArrayRef{}); if (!ForeachInputs.empty()) { diff --git a/clang/lib/Driver/ToolChains/SYCL.h b/clang/lib/Driver/ToolChains/SYCL.h index 777c1193c260f..7b38ba8c61e6f 100644 --- a/clang/lib/Driver/ToolChains/SYCL.h +++ b/clang/lib/Driver/ToolChains/SYCL.h @@ -82,6 +82,13 @@ StringRef resolveGenDevice(StringRef DeviceName); SmallString<64> getGenDeviceMacro(StringRef DeviceName); StringRef getGenGRFFlag(StringRef GRFMode); +// Returns the full path of the ocloc tool to be used for AOT compilation and +// for emitting the ocloc help information. A user provided --ocloc-path= is +// honored above all other lookup locations. If not found, the tool (ocloc) is +// returned with no directory. +const char *getOclocPath(const Compilation &C, const ToolChain &TC, + const llvm::opt::ArgList &Args); + // Prefix for GPU specific targets used for -fsycl-targets constexpr char IntelGPU[] = "intel_gpu_"; constexpr char NvidiaGPU[] = "nvidia_gpu_"; diff --git a/clang/test/Driver/sycl-ocloc-path.cpp b/clang/test/Driver/sycl-ocloc-path.cpp index 63210454f324f..fa29dcc54e759 100644 --- a/clang/test/Driver/sycl-ocloc-path.cpp +++ b/clang/test/Driver/sycl-ocloc-path.cpp @@ -57,7 +57,14 @@ // RUN: %clang -### -fsycl -fsycl-help=gen --ocloc-path=/my/ocloc/dir %s 2>&1 \ // RUN: | FileCheck -check-prefix=CHK-OCLOC-PATH-HELP %s // CHK-OCLOC-PATH-HELP: Emitting help information for ocloc -// CHK-OCLOC-PATH-HELP: "/my/ocloc/dir{{[/\\]+}}ocloc" "--help" +// CHK-OCLOC-PATH-HELP: "/my/ocloc/dir{{[/\\]+}}ocloc{{(\.exe)?}}" "--help" + +/// Check that the 'exe' name is used for windows when emitting the ocloc help +/// information. +// RUN: %clang -### -target x86_64-pc-windows-msvc -fsycl -fsycl-help=gen \ +// RUN: --ocloc-path=/my/ocloc/dir %s 2>&1 \ +// RUN: | FileCheck -check-prefix=CHK-OCLOC-PATH-HELP-WIN %s +// CHK-OCLOC-PATH-HELP-WIN: "/my/ocloc/dir{{[/\\]+}}ocloc.exe" "--help" /// Check the diagnostic emitted when the given directory does not contain a /// usable ocloc. Without -### the tool is actually launched, so the composed From d27f11df278f88bba7267b5cc7c8806564b0ad6d Mon Sep 17 00:00:00 2001 From: Michael D Toguchi Date: Thu, 13 Aug 2026 16:41:39 -0700 Subject: [PATCH 10/11] Sync findOcloc behaviors in tools --- clang/test/Driver/clang-linker-wrapper.cpp | 2 +- .../clang-linker-wrapper/ClangLinkerWrapper.cpp | 14 +++++++------- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/clang/test/Driver/clang-linker-wrapper.cpp b/clang/test/Driver/clang-linker-wrapper.cpp index 47a1aaab29a35..642e5201b0937 100644 --- a/clang/test/Driver/clang-linker-wrapper.cpp +++ b/clang/test/Driver/clang-linker-wrapper.cpp @@ -147,7 +147,7 @@ // Check the diagnostic emitted when ocloc cannot be found in the given // directory. // RUN: not clang-linker-wrapper --ocloc-path=%t.no-ocloc-here --linker-path=/usr/bin/ld -o /dev/null %t1.o 2>&1 | FileCheck -check-prefix=CHK-OCLOC-PATH-ERR %s -// CHK-OCLOC-PATH-ERR: Unable to find 'ocloc' in '{{.*}}no-ocloc-here' +// CHK-OCLOC-PATH-ERR: unable to find 'ocloc' in '{{.*}}no-ocloc-here' // Check that an empty --ocloc-path= is rejected. // RUN: not clang-linker-wrapper --ocloc-path= --linker-path=/usr/bin/ld -o /dev/null %t1.o --dry-run 2>&1 | FileCheck -check-prefix=CHK-OCLOC-PATH-NOARG %s diff --git a/clang/tools/clang-linker-wrapper/ClangLinkerWrapper.cpp b/clang/tools/clang-linker-wrapper/ClangLinkerWrapper.cpp index ae576ab05aeaa..676bc73a8f0ca 100644 --- a/clang/tools/clang-linker-wrapper/ClangLinkerWrapper.cpp +++ b/clang/tools/clang-linker-wrapper/ClangLinkerWrapper.cpp @@ -334,7 +334,7 @@ Expected findProgram(StringRef Name, ArrayRef Paths) { return Name.str(); if (!Path) return createStringError(Path.getError(), - "Unable to find '" + Name + "' in path"); + "unable to find '" + Name + "' in path"); return *Path; } @@ -345,17 +345,17 @@ Expected findOcloc(const ArgList &Args) { if (Dir.empty()) return createStringError("no directory given for '" + A->getSpelling() + "'"); - // Only look in the given directory. The tool name is resolved by - // findProgramByName, which takes care of any platform specific executable - // extension. - if (ErrorOr Path = sys::findProgramByName("ocloc", {Dir})) - return *Path; if (DryRun) { SmallString<128> OclocPath(Dir); sys::path::append(OclocPath, "ocloc"); return std::string(OclocPath); } - return createStringError("Unable to find 'ocloc' in '" + Dir + "'"); + // Only look in the given directory. The tool name is resolved by + // findProgramByName, which takes care of any platform specific executable + // extension. + if (ErrorOr Path = sys::findProgramByName("ocloc", {Dir})) + return *Path; + return createStringError("unable to find 'ocloc' in '" + Dir + "'"); } return findProgram("ocloc", {getExecutableDir("ocloc")}); } From cb367d57e021c468d7640468f410e488cdcd5fa3 Mon Sep 17 00:00:00 2001 From: Michael D Toguchi Date: Thu, 13 Aug 2026 16:53:58 -0700 Subject: [PATCH 11/11] Add appropriate documentation for the new option --- clang/docs/ClangLinkerWrapper.rst | 1 + clang/docs/ClangSYCLLinker.rst | 1 + clang/include/clang/Options/Options.td | 2 +- sycl/doc/UsersManual.md | 5 +++++ 4 files changed, 8 insertions(+), 1 deletion(-) diff --git a/clang/docs/ClangLinkerWrapper.rst b/clang/docs/ClangLinkerWrapper.rst index 6e3c6d573ce5f..cc623cfc53eae 100644 --- a/clang/docs/ClangLinkerWrapper.rst +++ b/clang/docs/ClangLinkerWrapper.rst @@ -44,6 +44,7 @@ only for the linker wrapper will be forwarded to the wrapped linker job. --linker-path= The linker executable to invoke -L Add to the library search path -l Search for library + --ocloc-path= Path to the ocloc tool used for Intel GPU AOT compilation --opt-level= Optimization level for LTO --override-image= diff --git a/clang/docs/ClangSYCLLinker.rst b/clang/docs/ClangSYCLLinker.rst index a2a7052cafffc..912f96346d536 100644 --- a/clang/docs/ClangSYCLLinker.rst +++ b/clang/docs/ClangSYCLLinker.rst @@ -56,6 +56,7 @@ be passed down to downstream AOT compilation tools like 'ocloc' and 'opencl-aot' -u Force undefined symbol during linking --module-split-mode= Module split mode: 'translation_unit' (default), 'kernel', or 'link_unit' --ocloc-options= Options passed to ocloc for Intel GPU AOT compilation + --ocloc-path= Path to the ocloc tool used for Intel GPU AOT compilation --opencl-aot-options= Options passed to opencl-aot for Intel CPU AOT compilation -o Path to file to write output --save-temps Save intermediate results diff --git a/clang/include/clang/Options/Options.td b/clang/include/clang/Options/Options.td index 5f1385acec2fc..0c1a2bb57a374 100644 --- a/clang/include/clang/Options/Options.td +++ b/clang/include/clang/Options/Options.td @@ -7905,7 +7905,7 @@ def fsycl_help : Flag<["-"], "fsycl-help">, Alias, HelpText<"Emit help information from all of the offline compilation tools">; def ocloc_path_EQ : Joined<["--"], "ocloc-path=">, Visibility<[ClangOption, CLOption]>, - Flags<[NoXarchOption, NoArgumentUnused]>, MetaVarName<"">, + Flags<[NoXarchOption, NoArgumentUnused]>, MetaVarName<"">, HelpText<"Path to the ocloc tool, which is used for ahead of time " "compilation targeting Intel GPUs">; def fsycl_libspirv_path_EQ : Joined<["-"], "fsycl-libspirv-path=">, diff --git a/sycl/doc/UsersManual.md b/sycl/doc/UsersManual.md index 68ce2b8d6456c..90f8e121c19c2 100644 --- a/sycl/doc/UsersManual.md +++ b/sycl/doc/UsersManual.md @@ -265,6 +265,11 @@ and not recommended to use in production environment. Pass "options" to the device code linker, when linking multiple device object modules. T is specific target device triple. +**`--ocloc-path=`** + + Path to the `ocloc` tool, which is used for ahead of time compilation + targeting Intel GPUs. + ## Link options **`-fsycl-link`**