Skip to content
Open
5 changes: 5 additions & 0 deletions clang/include/clang/Options/Options.td
Original file line number Diff line number Diff line change
Expand Up @@ -7903,6 +7903,11 @@ def fsycl_help_EQ
def fsycl_help : Flag<["-"], "fsycl-help">, Alias<fsycl_help_EQ>,
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<"<path>">,
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">,
Expand Down
38 changes: 30 additions & 8 deletions clang/lib/Driver/Driver.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -316,6 +316,16 @@ InputArgList Driver::ParseArgStrings(ArrayRef<const char *> 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)) {
Expand Down Expand Up @@ -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<std::tuple<llvm::Triple, StringRef, StringRef, StringRef>, 4>
HelpArgs;
// Populate the vector with the tools and help options
Expand All @@ -2823,15 +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<StringRef> ToolArgs = {std::get<1>(HA), std::get<2>(HA),
std::vector<StringRef> ToolArgs = {ToolName, std::get<2>(HA),
std::get<3>(HA)};
SmallString<128> ExecPath(
C.getDefaultToolChain().GetProgramPath(std::get<1>(HA).data()));
SmallString<128> ExecPath;
// 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] << "\"";
Expand All @@ -2841,12 +2862,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;
}
}

Expand Down
5 changes: 5 additions & 0 deletions clang/lib/Driver/ToolChains/Clang.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -12132,6 +12132,11 @@ void LinkerWrapper::ConstructJob(Compilation &C, const JobAction &JA,
// Add any SYCL offloading specific options to the clang-linker-wrapper
if (C.hasOffloadToolChain<Action::OFK_SYCL>()) {

// 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()));

if (Args.hasArg(options::OPT_fsycl_link_EQ))
CmdArgs.push_back(Args.MakeArgString("--sycl-device-link"));

Expand Down
4 changes: 4 additions & 0 deletions clang/lib/Driver/ToolChains/SPIRV.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -186,6 +186,10 @@ 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");
// 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()));
} 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();
Expand Down
19 changes: 15 additions & 4 deletions clang/lib/Driver/ToolChains/SYCL.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1046,14 +1046,27 @@ 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<Action::OFK_Host>();
if (HostTC->getTriple().isWindowsMSVCEnvironment())
ExeName.append(".exe");
return C.getArgs().MakeArgString(ExeName);
}

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.
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));

@YuriPlyakhin YuriPlyakhin Aug 12, 2026

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Empty value ( --ocloc-path=) behaves differently across tools/offloading models (SYCL.cpp vs. clang-linker-wrapper/clang-sycl-linker).

Here, it silently falls back to a PATH lookup at exec time.

In clang-linker-wrapper/clang-sycl-linker it will be a hard-error: Unable to find 'ocloc' in ''.

Given the stated contract is "the user provided location always wins", I think, it makes sense to also reject an empty value in the driver rather than letting it mean two different things depending on the tool.

}

// 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) {
Expand Down Expand Up @@ -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<Command>(JA, *this, ResponseFileSupport::None(),
Exec, CmdArgs, ArrayRef<InputInfo>{});
if (!ForeachInputs.empty()) {
Expand Down
7 changes: 7 additions & 0 deletions clang/lib/Driver/ToolChains/SYCL.h
Original file line number Diff line number Diff line change
Expand Up @@ -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_";
Expand Down
15 changes: 15 additions & 0 deletions clang/test/Driver/clang-linker-wrapper.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,21 @@
// 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 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.
Expand Down
92 changes: 92 additions & 0 deletions clang/test/Driver/sycl-ocloc-path.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
///
/// 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{{(\.exe)?}}" "-output"

/// Check that the user provided location wins over an ocloc that is visible
/// 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.
// 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{{(\.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
/// 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 \
// 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
16 changes: 16 additions & 0 deletions clang/test/OffloadTools/clang-sycl-linker/basic.ll
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,22 @@
; 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 --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 --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 \
Expand Down
25 changes: 23 additions & 2 deletions clang/tools/clang-linker-wrapper/ClangLinkerWrapper.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -338,6 +338,28 @@ Expected<std::string> findProgram(StringRef Name, ArrayRef<StringRef> Paths) {
return *Path;
}

/// Locate the 'ocloc' tool used for Intel GPU AOT compilation.
Expected<std::string> findOcloc(const ArgList &Args) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Implementation in ClangLinkerWrapper.cpp and ClangSYCLLinker.cpp - near-identical, but differ in some subtle things:

  1. The two copies check DryRun on opposite sides of the findProgramByName call. In the wrapper, a --dry-run in an environment where ocloc does exist in the given directory hits the filesystem and returns the resolved path; in clang-sycl-linker it never does.
  2. Diagnostic capitalization is different (Unable to find vs unable to find).

Could you please align implementations?

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.
if (ErrorOr<std::string> 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() ||
Expand Down Expand Up @@ -1081,8 +1103,7 @@ runAOTCompileIntelGPU(StringRef InputFile, const ArgList &Args,
const llvm::Triple Triple(Args.getLastArgValue(OPT_triple_EQ));
StringRef Arch(Args.getLastArgValue(OPT_arch_EQ));
SmallVector<StringRef, 8> CmdArgs;
Expected<std::string> OclocPath =
findProgram("ocloc", {getExecutableDir("ocloc")});
Expected<std::string> OclocPath = findOcloc(Args);
if (!OclocPath)
return OclocPath.takeError();

Expand Down
4 changes: 4 additions & 0 deletions clang/tools/clang-linker-wrapper/LinkerWrapperOpts.td
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,10 @@ def linker_path_EQ : Joined<["--"], "linker-path=">,
def cuda_path_EQ : Joined<["--"], "cuda-path=">,
Flags<[WrapperOnlyOption]>, MetaVarName<"<dir>">,
HelpText<"Set the system CUDA path">;
def ocloc_path_EQ : Joined<["--"], "ocloc-path=">,

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please, update documentation for both: clang-linker-wrapper and clang-sycl-linker to document new options (also driver maybe?)
ClangLinkerWrapper.rst
ClangSYCLLinker.rst
sycl/doc/UsersManual.md

Also Options WG???

Flags<[WrapperOnlyOption]>, MetaVarName<"<dir>">,
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<"<triple>">,
Expand Down
25 changes: 23 additions & 2 deletions clang/tools/clang-sycl-linker/ClangSYCLLinker.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -169,6 +169,28 @@ static Expected<std::string> findProgram(const ArgList &Args, StringRef Name,
return *Path;
}

/// Locate the 'ocloc' tool used for Intel GPU AOT compilation.
static Expected<std::string> 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");
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<std::string> 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<StringRef> CmdArgs) {
if (CmdArgs.empty())
return;
Expand Down Expand Up @@ -710,8 +732,7 @@ static Error runAOTCompileIntelCPU(StringRef InputFile, StringRef OutputFile,
static Error runAOTCompileIntelGPU(StringRef InputFile, StringRef OutputFile,
const ArgList &Args) {
SmallVector<StringRef, 8> CmdArgs;
Expected<std::string> OclocPath =
findProgram(Args, "ocloc", {getMainExecutable("ocloc")});
Expected<std::string> OclocPath = findOcloc(Args);
if (!OclocPath)
return OclocPath.takeError();

Expand Down
Loading
Loading