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 837407f0ba574..0c1a2bb57a374 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<"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/Driver.cpp b/clang/lib/Driver/Driver.cpp index 7a0d81674601d..f9e4749fdc4f9 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 @@ -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 ToolArgs = {std::get<1>(HA), std::get<2>(HA), + std::vector 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] << "\""; @@ -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; } } diff --git a/clang/lib/Driver/ToolChains/Clang.cpp b/clang/lib/Driver/ToolChains/Clang.cpp index 0aa88c190ad0f..a9648b250aecf 100644 --- a/clang/lib/Driver/ToolChains/Clang.cpp +++ b/clang/lib/Driver/ToolChains/Clang.cpp @@ -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()) { + // 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")); diff --git a/clang/lib/Driver/ToolChains/SPIRV.cpp b/clang/lib/Driver/ToolChains/SPIRV.cpp index 5cc1eec74c1cc..e132ff8a1a7d3 100644 --- a/clang/lib/Driver/ToolChains/SPIRV.cpp +++ b/clang/lib/Driver/ToolChains/SPIRV.cpp @@ -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(); diff --git a/clang/lib/Driver/ToolChains/SYCL.cpp b/clang/lib/Driver/ToolChains/SYCL.cpp index 084cf7614158b..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,6 +1054,19 @@ static const char *makeExeName(Compilation &C, StringRef Name) { 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)); +} + // 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..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/clang-linker-wrapper.cpp b/clang/test/Driver/clang-linker-wrapper.cpp index 9c20a19601bee..642e5201b0937 100644 --- a/clang/test/Driver/clang-linker-wrapper.cpp +++ b/clang/test/Driver/clang-linker-wrapper.cpp @@ -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. diff --git a/clang/test/Driver/sycl-ocloc-path.cpp b/clang/test/Driver/sycl-ocloc-path.cpp new file mode 100644 index 0000000000000..fa29dcc54e759 --- /dev/null +++ b/clang/test/Driver/sycl-ocloc-path.cpp @@ -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 diff --git a/clang/test/OffloadTools/clang-sycl-linker/basic.ll b/clang/test/OffloadTools/clang-sycl-linker/basic.ll index 448a166e0a5e1..ef8310f0167bb 100644 --- a/clang/test/OffloadTools/clang-sycl-linker/basic.ll +++ b/clang/test/OffloadTools/clang-sycl-linker/basic.ll @@ -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 \ diff --git a/clang/tools/clang-linker-wrapper/ClangLinkerWrapper.cpp b/clang/tools/clang-linker-wrapper/ClangLinkerWrapper.cpp index 59aa81ed94a74..676bc73a8f0ca 100644 --- a/clang/tools/clang-linker-wrapper/ClangLinkerWrapper.cpp +++ b/clang/tools/clang-linker-wrapper/ClangLinkerWrapper.cpp @@ -334,10 +334,32 @@ 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; } +/// 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(); + 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 Path = sys::findProgramByName("ocloc", {Dir})) + return *Path; + 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 +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 CmdArgs; - Expected OclocPath = - findProgram("ocloc", {getExecutableDir("ocloc")}); + Expected OclocPath = findOcloc(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..3b78f1fe125dc 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<"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/ClangSYCLLinker.cpp b/clang/tools/clang-sycl-linker/ClangSYCLLinker.cpp index 5e937507289ec..0341c35497a26 100644 --- a/clang/tools/clang-sycl-linker/ClangSYCLLinker.cpp +++ b/clang/tools/clang-sycl-linker/ClangSYCLLinker.cpp @@ -169,6 +169,28 @@ 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 (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 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 +732,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..ea21381486040 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<"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]>, HelpText<"Options passed to opencl-aot for Intel CPU AOT compilation">; 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`**