diff --git a/clang/lib/Driver/ToolChains/Clang.cpp b/clang/lib/Driver/ToolChains/Clang.cpp index dd11cd4f5d6f1..371f2460f5ee3 100644 --- a/clang/lib/Driver/ToolChains/Clang.cpp +++ b/clang/lib/Driver/ToolChains/Clang.cpp @@ -10257,6 +10257,18 @@ void OffloadPackager::ConstructJob(Compilation &C, const JobAction &JA, "kind=" + Kind.str(), }; + // When compiling like -fsycl-targets=spir64_gen -Xsycl-target-backend + // "-device pvc,bdw", the offloading arch will be "pvc,bdw", which + // contains a comma. Because the comma is used to separate fields + // within the --image option, we cannot pass arch=pvc,bdw directly. + // Instead, we pass it like arch=pvc,arch=bdw, then + // llvm-offload-binary joins them back to arch=pvc,bdw. + SmallVector Archs; + Arch.split(Archs, ','); + if (Archs.size() > 1) { + Parts[2] = "arch=" + llvm::join(Archs, ",arch="); + } + if (TC->getDriver().isUsingOffloadLTO()) for (StringRef Feature : FeatureArgs) Parts.emplace_back("feature=" + Feature.str()); @@ -10280,7 +10292,13 @@ void OffloadPackager::ConstructJob(Compilation &C, const JobAction &JA, AL += " "; AL += A; } - Parts.emplace_back(C.getArgs().MakeArgString(Twine(Opt) + AL)); + // As mentioned earlier, we cannot pass a value with commas directly, + // but llvm-offload-binary joins multiple occurrences of the same + // option separated by commas, so we split the value on + // all commas and pass them as separate arguments. + for (StringRef Split : llvm::split(AL, ',')) { + Parts.emplace_back(C.getArgs().MakeArgString(Twine(Opt) + Split)); + } }; const ArgList &Args = C.getArgsForToolChain(nullptr, StringRef(), Action::OFK_SYCL); @@ -10289,10 +10307,10 @@ void OffloadPackager::ConstructJob(Compilation &C, const JobAction &JA, static_cast(*TC); SYCLTC.AddImpliedTargetArgs(TC->getTriple(), Args, BuildArgs, JA, *HostTC, Arch); - SYCLTC.TranslateBackendTargetArgs(TC->getTriple(), Args, BuildArgs, Arch); + SYCLTC.TranslateBackendTargetArgs(TC->getTriple(), Args, BuildArgs); createArgString("compile-opts="); BuildArgs.clear(); - SYCLTC.TranslateLinkerTargetArgs(TC->getTriple(), Args, BuildArgs, Arch); + SYCLTC.TranslateLinkerTargetArgs(TC->getTriple(), Args, BuildArgs); createArgString("link-opts="); } diff --git a/clang/test/Driver/sycl-ftarget-compile-fast.cpp b/clang/test/Driver/sycl-ftarget-compile-fast.cpp index e63801cdf2ecd..3a8c795676a9c 100644 --- a/clang/test/Driver/sycl-ftarget-compile-fast.cpp +++ b/clang/test/Driver/sycl-ftarget-compile-fast.cpp @@ -8,7 +8,7 @@ // RUN: | FileCheck -check-prefix=TARGET_COMPILE_FAST_GEN %s // TARGET_COMPILE_FAST_GEN: llvm-offload-binary -// TARGET_COMPILE_FAST_GEN: compile-opts={{.*}}-options -igc_opts 'PartitionUnit=1,SubroutineThreshold=50000' +// TARGET_COMPILE_FAST_GEN: compile-opts={{.*}}-options -igc_opts 'PartitionUnit=1,compile-opts=SubroutineThreshold=50000' // RUN: %clang -### -target x86_64-unknown-linux-gnu -fsycl --offload-new-driver \ // RUN: -ftarget-compile-fast %s 2>&1 \ diff --git a/clang/test/Driver/sycl-offload-new-driver.c b/clang/test/Driver/sycl-offload-new-driver.c index 551198661b486..e06633f5446e5 100644 --- a/clang/test/Driver/sycl-offload-new-driver.c +++ b/clang/test/Driver/sycl-offload-new-driver.c @@ -188,7 +188,7 @@ // RUN: %clangxx -fsycl -### -fsycl-targets=spir64_gen --offload-new-driver \ // RUN: -Xsycl-target-backend=spir64_gen "-device pvc,bdw" %s 2>&1 \ // RUN: | FileCheck -check-prefix COMMA_FILE %s -// COMMA_FILE: llvm-offload-binary{{.*}} "--image=file={{.*}}pvc@bdw{{.*}},triple=spir64_gen-unknown-unknown,arch=pvc,bdw,kind=sycl,compile-opts=-device_options pvc -ze-intel-enable-auto-large-GRF-mode" +// COMMA_FILE: llvm-offload-binary{{.*}} "--image=file={{.*}}pvc@bdw{{.*}},triple=spir64_gen-unknown-unknown,arch=pvc,arch=bdw,kind=sycl,compile-opts=-device_options pvc -ze-intel-enable-auto-large-GRF-mode -device pvc,compile-opts=bdw" /// Verify the arch value for the packager is populated with different /// scenarios for spir64_gen @@ -212,6 +212,52 @@ // RUN: | FileCheck -check-prefix ARCH_CHECK %s // ARCH_CHECK: llvm-offload-binary{{.*}} "--image=file={{.*}}triple=spir64_gen-unknown-unknown,arch=bdw,kind=sycl{{.*}}" +// Verify when a comma-separated list of architectures is provided in -device, they are +// passed to llvm-offload-binary correctly. +// RUN: %clangxx -fsycl -### -fsycl-targets=spir64_gen --offload-new-driver \ +// RUN: -Xsycl-target-backend "-device pvc,bdw" %s 2>&1 \ +// RUN: | FileCheck -check-prefix MULTI_ARCH %s +// RUN: %clangxx -fsycl -### -fsycl-targets=spir64_gen --offload-new-driver \ +// RUN: -Xsycl-target-backend=spir64_gen "-device pvc,bdw" %s 2>&1 \ +// RUN: | FileCheck -check-prefix MULTI_ARCH %s +// RUN: %clangxx -fsycl -### -fsycl-targets=spir64_gen --offload-new-driver \ +// RUN: -Xs "-device pvc,bdw" %s 2>&1 \ +// RUN: | FileCheck -check-prefix MULTI_ARCH %s +// MULTI_ARCH: llvm-offload-binary{{.*}} "--image=file={{.*}}triple=spir64_gen-unknown-unknown,arch=pvc,arch=bdw,kind=sycl +// MULTI_ARCH-SAME: compile-opts=-device_options pvc -ze-intel-enable-auto-large-GRF-mode -device pvc,compile-opts=bdw" + +// Verify that when an object produced by llvm-offload-binary with multiple Intel GPU architectures +// clang-linker-wrapper will call ocloc with -device listing all architectures. +// RUN: %clangxx -fsycl -fsycl-targets=spir64_gen --offload-new-driver \ +// RUN: -Xsycl-target-backend=spir64_gen "-device pvc,bdw" -c %s -o %t_multiarch_test.o +// RUN: clang-linker-wrapper --dry-run --linker-path=/usr/bin/ld \ +// RUN: --host-triple=x86_64-unknown-linux-gnu %t_multiarch_test.o 2>&1 \ +// RUN: | FileCheck -check-prefix=OCLOC_MULTI_ARCH %s +// OCLOC_MULTI_ARCH: ocloc{{.*}}-device pvc,bdw + +// Verify for multiple targets with -Xsycl-target-backend= with commas in the values +// are passed correctly to llvm-offload-binary. +// RUN: %clangxx -fsycl -### --offload-new-driver \ +// RUN: -fsycl-targets=nvptx64-nvidia-cuda,amdgcn-amd-amdhsa,spir64_gen \ +// RUN: -Xsycl-target-backend=amdgcn-amd-amdhsa --offload-arch=gfx908,gfx1010 \ +// RUN: -Xsycl-target-backend=nvptx64-nvidia-cuda --offload-arch=sm_86,sm_87,sm_89 \ +// RUN: -Xsycl-target-backend=spir64_gen "-device pvc,bdw" \ +// RUN: -Xsycl-target-linker=spir64_gen "-DFOO,BAR" \ +// RUN: -nogpulib %s 2>&1 | FileCheck -check-prefix=MULTI_ARCH2 %s +// MULTI_ARCH2: llvm-offload-binary{{.*}} "--image=file={{.*}}triple=amdgcn-amd-amdhsa,arch=gfx1010,kind=sycl,compile-opts=--offload-arch=gfx908,compile-opts=gfx1010" +// MULTI_ARCH2-SAME: "--image=file={{.*}}triple=amdgcn-amd-amdhsa,arch=gfx908,kind=sycl,compile-opts=--offload-arch=gfx908,compile-opts=gfx1010" +// MULTI_ARCH2-SAME: "--image=file={{.*}}triple=nvptx64-nvidia-cuda,arch=sm_86,kind=sycl,compile-opts=--offload-arch=sm_86,compile-opts=sm_87,compile-opts=sm_89" +// MULTI_ARCH2-SAME: "--image=file={{.*}}triple=nvptx64-nvidia-cuda,arch=sm_87,kind=sycl,compile-opts=--offload-arch=sm_86,compile-opts=sm_87,compile-opts=sm_89" +// MULTI_ARCH2-SAME: "--image=file={{.*}}triple=nvptx64-nvidia-cuda,arch=sm_89,kind=sycl,compile-opts=--offload-arch=sm_86,compile-opts=sm_87,compile-opts=sm_89" +// MULTI_ARCH2-SAME: "--image=file={{.*}}triple=spir64_gen-unknown-unknown,arch=pvc,arch=bdw,kind=sycl,compile-opts=-device_options pvc -ze-intel-enable-auto-large-GRF-mode -device pvc,compile-opts=bdw,link-opts=-DFOO,link-opts=BAR" + +// Verify that the driver correctly handles link-opt and compile-opt values with commas +// RUN: %clangxx -fsycl -### -fsycl-targets=spir64_gen --offload-new-driver \ +// RUN: -Xsycl-target-backend "-device bdw -FOO a,b" \ +// RUN: -Xsycl-target-linker "-BAR x,y" %s 2>&1 \ +// RUN: | FileCheck -check-prefix COMMA_OPTS %s +// COMMA_OPTS: llvm-offload-binary{{.*}} "--image=file={{.*}}triple=spir64_gen-unknown-unknown,arch=bdw,kind=sycl,compile-opts=-device bdw -FOO a,compile-opts=b,link-opts=-BAR x,link-opts=y" + /// Verify that --cuda-path is passed to clang-linker-wrapper for SYCL offload // RUN: %clangxx -fsycl -### -fsycl-targets=nvptx64-nvidia-cuda -fno-sycl-libspirv \ // RUN: --cuda-gpu-arch=sm_20 --cuda-path=%S/Inputs/CUDA_80/usr/local/cuda %s \ diff --git a/clang/tools/clang-linker-wrapper/ClangLinkerWrapper.cpp b/clang/tools/clang-linker-wrapper/ClangLinkerWrapper.cpp index 679ecf8374804..1132996da575e 100644 --- a/clang/tools/clang-linker-wrapper/ClangLinkerWrapper.cpp +++ b/clang/tools/clang-linker-wrapper/ClangLinkerWrapper.cpp @@ -943,24 +943,29 @@ static void addSYCLBackendOptions(const ArgList &Args, if (IsCPU) { BackendOptions.split(CmdArgs, " ", /*MaxSplit=*/-1, /*KeepEmpty=*/false); } else { - // ocloc -options args need to be comma separated, e.g. `-options - // "-g,-cl-opt-disable"`. Otherwise, only the first arg is processed by - // ocloc as an arg for -options, and the rest are processed as standalone - // flags, possibly leading to errors. + // ocloc -options takes arguments in the form of '-options "-g + // -cl-opt-disable"' where each argument is separated with spaces. // split function here returns a pair with everything before the separator // ("-options") in the first member of the pair, and everything after the // separator in the second part of the pair. The separator is not included // in any of them. auto [BeforeOptions, AfterOptions] = BackendOptions.split("-options "); // Only add if not empty, an empty arg can lead to ocloc errors. - if (!BeforeOptions.empty()) - CmdArgs.push_back(BeforeOptions); + if (!BeforeOptions.empty()) { + SmallVector BeforeArgs; + BeforeOptions.split(BeforeArgs, " ", /*MaxSplit=*/-1, + /*KeepEmpty=*/false); + for (const auto &string : BeforeArgs) { + CmdArgs.push_back(string); + } + } if (!AfterOptions.empty()) { - // Separator not included by the split function, so explicitly added here. CmdArgs.push_back("-options"); - std::string Replace = AfterOptions.str(); - std::replace(Replace.begin(), Replace.end(), ' ', ','); - CmdArgs.push_back(Args.MakeArgString(Replace)); + // Split the options string by spaces and rejoin to normalize whitespace + SmallVector AfterArgs; + AfterOptions.split(AfterArgs, " ", /*MaxSplit=*/-1, /*KeepEmpty=*/false); + std::string JoinedOptions = llvm::join(AfterArgs, " "); + CmdArgs.push_back(Args.MakeArgString(JoinedOptions)); } } @@ -1441,9 +1446,8 @@ static Expected linkDevice(ArrayRef InputFiles, if (ExtractedDeviceLibFiles.empty()) { // TODO: Add NVPTX when ready if (Triple.isSPIROrSPIRV()) - return createStringError( - inconvertibleErrorCode(), - " SYCL device library file list cannot be empty."); + WithColor::warning(errs(), LinkerExecutable) + << "SYCL device library file list is empty\n"; return *LinkedFile; }