Skip to content
24 changes: 21 additions & 3 deletions clang/lib/Driver/ToolChains/Clang.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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<StringRef> 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());
Expand All @@ -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);
Expand All @@ -10289,10 +10307,10 @@ void OffloadPackager::ConstructJob(Compilation &C, const JobAction &JA,
static_cast<const toolchains::SYCLToolChain &>(*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=");
}

Expand Down
2 changes: 1 addition & 1 deletion clang/test/Driver/sycl-ftarget-compile-fast.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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 \
Expand Down
48 changes: 47 additions & 1 deletion clang/test/Driver/sycl-offload-new-driver.c
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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 \
Expand Down
30 changes: 17 additions & 13 deletions clang/tools/clang-linker-wrapper/ClangLinkerWrapper.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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
Copy link
Contributor

Choose a reason for hiding this comment

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

So, Yixing's change should be merged first, right?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yes, I forgot to add a comment here on the PR, but I needed Yixing's change to pass testing.

Copy link
Contributor

Choose a reason for hiding this comment

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

ok. I think if this change is ready first, we should go ahead and merge it. It doesn't matter, which change goes first in. Both are required anyway.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Sounds good. FYI @YixingZhang007, I think this should merge cleanly with your changes but just letting you know.

// -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<StringRef, 8> 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
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
// Split the options string by spaces and rejoin to normalize whitespace
// Split the options string by spaces and rejoin to normalize whitespace.

SmallVector<StringRef, 8> AfterArgs;
AfterOptions.split(AfterArgs, " ", /*MaxSplit=*/-1, /*KeepEmpty=*/false);
std::string JoinedOptions = llvm::join(AfterArgs, " ");
CmdArgs.push_back(Args.MakeArgString(JoinedOptions));
}
}

Expand Down Expand Up @@ -1441,9 +1446,8 @@ static Expected<StringRef> linkDevice(ArrayRef<StringRef> 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;
}

Expand Down
Loading