Skip to content
Merged
55 changes: 28 additions & 27 deletions clang/lib/Driver/ToolChains/Clang.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -10522,6 +10522,17 @@ static void addRunTimeWrapperOpts(Compilation &C,
// Grab any Target specific options that need to be added to the wrapper
// information.
ArgStringList BuildArgs;
// FIXME: BuildArgs is joined with spaces below into a single
// -compile-opts=/-link-opts= string embedded in the image descriptor, then
// later re-split on spaces by clang-linker-wrapper for AOT triples (see
// addOclocOptions/runAOTCompileIntelCPU in
// clang/tools/clang-linker-wrapper/ClangLinkerWrapper.cpp). Any individual
// token containing an embedded space is corrupted by this round trip
// unless it happens to fall inside the pre-existing "-options \"...\""
// wrapper convention. Forwarding these as individual tokens instead of a
// joined string (as is already done for the CLI-supplied
// --device-compiler=/--device-linker= counterpart via AOTDeviceArgs) would
// avoid this.
auto createArgString = [&](const char *Opt) {
if (BuildArgs.empty())
return;
Expand Down Expand Up @@ -10840,6 +10851,17 @@ void OffloadPackager::ConstructJob(Compilation &C, const JobAction &JA,
// compilers and the clang-offload-wrapper in the case of SYCL offloading.
if (OffloadAction->getOffloadingDeviceKind() == Action::OFK_SYCL) {
ArgStringList BuildArgs;
// FIXME: BuildArgs is joined with spaces into a single compile-opts=/
// link-opts= string below, then later re-split on spaces by
// clang-linker-wrapper (see addOclocOptions/runAOTCompileIntelCPU in
// clang/tools/clang-linker-wrapper/ClangLinkerWrapper.cpp). Any
// individual token containing an embedded space (e.g. from
// -Xsycl-target-backend "-abc 'multi word'") is corrupted by this
// round trip unless it happens to fall inside the pre-existing
// "-options \"...\"" wrapper convention. The CLI-supplied counterpart
// of these options (--device-compiler=/--device-linker=) avoids this
// by forwarding individual tokens (AOTDeviceArgs); the image-embedded
// path here still does not.
auto createArgString = [&](const char *Opt) {
if (BuildArgs.empty())
return;
Expand Down Expand Up @@ -12008,7 +12030,6 @@ void LinkerWrapper::ConstructJob(Compilation &C, const JobAction &JA,
Args);
SYCLInstallation.getSYCLDeviceLibPath(LibLocCandidates);
SmallString<128> LibName("libsycl-crt.bc");
bool IsNewOffload = D.getUseNewOffloadingDriver();
for (const auto &LibLoc : LibLocCandidates) {
SmallString<128> FullLibName(LibLoc);
llvm::sys::path::append(FullLibName, LibName);
Expand All @@ -12028,12 +12049,6 @@ void LinkerWrapper::ConstructJob(Compilation &C, const JobAction &JA,
// targets (NVPTX, AMD) pass device libraries to clang-linker-wrapper.
SmallVector<std::string, 4> BCLibList;

auto appendToList = [](SmallString<256> &List, const Twine &Arg) {
if (List.size() > 0)
List += ",";
List += Arg.str();
};

auto ToolChainRange = C.getOffloadToolChains<Action::OFK_SYCL>();
for (const auto &[Kind, TC] :
llvm::make_range(ToolChainRange.first, ToolChainRange.second)) {
Expand Down Expand Up @@ -12190,33 +12205,19 @@ void LinkerWrapper::ConstructJob(Compilation &C, const JobAction &JA,
if (!TC->getTriple().isSPIROrSPIRV())
continue;
ArgStringList BuildArgs;
SmallString<128> BackendOptString;
SmallString<128> LinkOptString;
SYCLTC.TranslateBackendTargetArgs(TC->getTriple(), Args, BuildArgs);
for (const auto &A : BuildArgs)
appendOption(BackendOptString, A);
CmdArgs.push_back(
Args.MakeArgString("--device-compiler=" +
Action::GetOffloadKindName(Action::OFK_SYCL) +
":" + TC->getTripleString() + "=" + A));

BuildArgs.clear();
SYCLTC.TranslateLinkerTargetArgs(TC->getTriple(), Args, BuildArgs);
for (const auto &A : BuildArgs) {
if (TC->getTriple().getSubArch() == llvm::Triple::NoSubArch)
appendOption(LinkOptString, A);
else
// For AOT, combine the Backend and Linker strings into one.
appendOption(BackendOptString, A);
}

if (!BackendOptString.empty()) {
CmdArgs.push_back(Args.MakeArgString(
"--device-compiler=" +
Action::GetOffloadKindName(Action::OFK_SYCL) + ":" +
TC->getTripleString() + "=" + BackendOptString));
}
if (!LinkOptString.empty()) {
for (const auto &A : BuildArgs)
CmdArgs.push_back(Args.MakeArgString(
"--device-linker=" + Action::GetOffloadKindName(Action::OFK_SYCL) +
":" + TC->getTripleString() + "=" + LinkOptString));
}
":" + TC->getTripleString() + "=" + A));
}

// Add option to enable creating of the .syclbin file.
Expand Down
22 changes: 13 additions & 9 deletions clang/test/Driver/clang-linker-wrapper.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -126,11 +126,14 @@
// CHK-CMDS-AOT-GEN-NEXT: clang{{.*}} -c -o [[LLCOUT:.*]].o [[WRAPPEROUT]].bc
// CHK-CMDS-AOT-GEN-NEXT: "{{.*}}/ld" -- HOST_LINKER_FLAGS -dynamic-linker HOST_DYN_LIB -o a.out [[LLCOUT]].o HOST_LIB_PATH HOST_STAT_LIB {{.*}}.o

// Check that when "--device-compiler=triple=-device pvc" is specified in clang-linker-wrapper
// (happen when AOT device is specified via -Xsycl-target-backend '-device pvc' in clang),
// the target is not passed to sycl-post-link for filtering.
// RUN: clang-linker-wrapper -sycl-embed-ir --bitcode-library=spir64_gen-unknown-unknown=%t1.devicelib.bc -sycl-post-link-options="SYCL_POST_LINK_OPTIONS" -llvm-spirv-options="LLVM_SPIRV_OPTIONS" "--host-triple=x86_64-unknown-linux-gnu" "--device-compiler=spir64_gen-unknown-unknown=-device pvc" "--linker-path=/usr/bin/ld" "--" HOST_LINKER_FLAGS "-dynamic-linker" HOST_DYN_LIB "-o" "a.out" HOST_LIB_PATH HOST_STAT_LIB %t1.o --dry-run 2>&1 | FileCheck -check-prefix=CHK-NO-CMDS-AOT-GEN %s
// CHK-NO-CMDS-AOT-GEN: sycl-post-link{{.*}} SYCL_POST_LINK_OPTIONS -o {{[^,]*}}.table {{.*}}.bc

@YuriPlyakhin YuriPlyakhin Jul 18, 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.

The removed CHK-NO-CMDS-AOT-GEN check verified that -device suppresses the intel_gpu_... prefix passed to sycl-post-link. The IsDevicePassedWithSyclTargetBackend path is now untested.
Please add a sycl-post-link{{.*}} -o {{[^,]*}}.table check to the new RUN line.

// Check that when "-device pvc" specification, split across two separate
// --device-linker= arguments (each --device-compiler=/--device-linker= CLI
// occurrence becomes exactly one ocloc argv entry, so a multi-token value
// like "-device pvc" must be supplied as separate occurrences, not as one
// value containing an embedded space), is reconstructed correctly in the
// ocloc invocation.
Comment on lines +129 to +134

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.

nit

Suggested change
// Check that when "-device pvc" specification, split across two separate
// --device-linker= arguments (each --device-compiler=/--device-linker= CLI
// occurrence becomes exactly one ocloc argv entry, so a multi-token value
// like "-device pvc" must be supplied as separate occurrences, not as one
// value containing an embedded space), is reconstructed correctly in the
// ocloc invocation.
// Check that a "-device pvc" specification split across two separate
// --device-linker= arguments (each --device-compiler=/--device-linker= CLI
// occurrence becomes exactly one ocloc argv entry, so a multi-token value
// must be supplied as separate occurrences)
// is reconstructed correctly in the ocloc invocation.

// RUN: clang-linker-wrapper --device-linker=spir64_gen-unknown-unknown=-device --device-linker=spir64_gen-unknown-unknown=pvc --linker-path=/usr/bin/ld -o /dev/null %t1.o --dry-run 2>&1 | FileCheck -check-prefix=CHK-NO-CMDS-AOT-GEN-LINKERARG %s
// CHK-NO-CMDS-AOT-GEN-LINKERARG: ocloc{{.*}} -device pvc -output

/// Check for list of commands for standalone clang-linker-wrapper run for sycl (AOT for Intel CPU)
// -------
Expand Down Expand Up @@ -329,9 +332,10 @@
// RUN: --bitcode-library=spir64_gen-unknown-unknown=%t1.devicelib.bc \
// RUN: %t.aot.o -o %t.out 2>&1 --linker-path="/usr/bin/ld" | FileCheck %s --check-prefix=CHECK-COMPILE-LINK-OPTS-AOT
//
// Check that in AOT case backend options are passed to ocloc and are not passed to offload wrapper
// because SYCL Runtime can't make any use of it in AOT case.
// CHECK-COMPILE-LINK-OPTS-AOT: ocloc{{.*}} -device pvc ccc ccc -output
// Check that in AOT case backend and linker options are passed to ocloc and

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.

This PR also changes AOT behavior: image-embedded link-opts are now forwarded to ocloc, whereas before only compile-opts reached the AOT backend. This is reasonable, but it's a behavior change independent of the token-splitting fix — please call it out in the PR description.

// are not passed to offload wrapper because SYCL runtime can't make any use of
// it in AOT case.
// CHECK-COMPILE-LINK-OPTS-AOT: ocloc{{.*}} -device pvc ccc ccc ddd ddd -output
// CHECK-COMPILE-LINK-OPTS-AOT: offload-wrapper: {{.*}} compile-opts: , link-opts:

// TODO: The following check is turned off because clang-linker-wrapper doesn't support
Expand All @@ -343,7 +347,7 @@
// rUN: %t.jit_and_aot.o -o %t.out 2>&1 --linker-path="/usr/bin/ld" | FileCheck %s --check-prefix=CHECK-COMPILE-LINK-OPTS-JIT-AND-AOT

// cHECK-COMPILE-LINK-OPTS-JIT-AND-AOT: offload-wrapper: {{.*}} compile-opts: aaa aaa, link-opts: bbb bbb
// cHECK-COMPILE-LINK-OPTS-JIT-AND-AOT: ocloc{{.*}} -device pvc ccc ccc
// cHECK-COMPILE-LINK-OPTS-JIT-AND-AOT: ocloc{{.*}} -device pvc ccc ccc ddd ddd
// cHECK-COMPILE-LINK-OPTS-JIT-AND-AOT: offload-wrapper: {{.*}} compile-opts: , link-opts:

// Check that missed triple in image causes an error.
Expand Down
12 changes: 12 additions & 0 deletions clang/test/Driver/sycl-offload-new-driver.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,18 @@
// WRAPPER_OPTIONS_BACKEND_AOT-SAME: "--device-compiler=sycl:spir64_gen-unknown-unknown=-backend-gen-opt"
// WRAPPER_OPTIONS_BACKEND_AOT-SAME: "--device-compiler=sycl:spir64_x86_64-unknown-unknown=-backend-cpu-opt"

/// Test that -Xsycl-target-backend and -Xsycl-target-linker options for an
/// AOT (ocloc) target are forwarded via --device-compiler=/--device-linker=
/// respectively, each token as its own argument, the same as for JIT
/// targets.
// RUN: %clangxx --target=x86_64-unknown-linux-gnu -fsycl --offload-new-driver --sysroot=%S/Inputs/SYCL \
// RUN: -fsycl-targets=intel_gpu_pvc \
// RUN: -Xsycl-target-backend -opt1 -Xsycl-target-linker -opt2 \
// RUN: -### %s 2>&1 \
// RUN: | FileCheck -check-prefix WRAPPER_OPTIONS_AOT_SEPARATE %s
// WRAPPER_OPTIONS_AOT_SEPARATE: clang-linker-wrapper{{.*}} "--device-compiler=sycl:spir64_gen-unknown-unknown=-opt1"
// WRAPPER_OPTIONS_AOT_SEPARATE-SAME: "--device-linker=sycl:spir64_gen-unknown-unknown=-opt2"

/// Verify arch settings for nvptx and amdgcn targets
// RUN: %clangxx -fsycl -### -fsycl-targets=amdgcn-amd-amdhsa -fno-sycl-libspirv \
// RUN: -nocudalib --offload-new-driver --sysroot=%S/Inputs/SYCL \
Expand Down
4 changes: 2 additions & 2 deletions clang/test/Driver/sycl-offload.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -328,7 +328,7 @@

// RUN: %clang -### -target x86_64-unknown-linux-gnu -fsycl --offload-new-driver --sysroot=%S/Inputs/SYCL -fsycl-targets=spir64-unknown-unknown -Xsycl-target-backend "-DFOO1 -DFOO2" %s 2>&1 \
// RUN: | FileCheck -check-prefix=CHK-TOOLS-OPTS %s
// CHK-TOOLS-OPTS: clang-linker-wrapper{{.*}} "--device-compiler=sycl:spir64-unknown-unknown=-DFOO1 -DFOO2"
// CHK-TOOLS-OPTS: clang-linker-wrapper{{.*}} "--device-compiler=sycl:spir64-unknown-unknown=-DFOO1" "--device-compiler=sycl:spir64-unknown-unknown=-DFOO2"

/// Check for implied options (-g -O0)
// RUN: %clang -### -target x86_64-unknown-linux-gnu -fsycl --offload-new-driver --sysroot=%S/Inputs/SYCL -fsycl-targets=spir64-unknown-unknown -g -O0 -Xsycl-target-backend "-DFOO1 -DFOO2" %s 2>&1 \
Expand All @@ -348,7 +348,7 @@

// RUN: %clang -### -target x86_64-unknown-linux-gnu -fsycl --offload-new-driver --sysroot=%S/Inputs/SYCL -fsycl-targets=spir64-unknown-unknown -Xsycl-target-linker "-DFOO1 -DFOO2" %s 2>&1 \
// RUN: | FileCheck -check-prefix=CHK-TOOLS-OPTS2 %s
// CHK-TOOLS-OPTS2: clang-linker-wrapper{{.*}} "--device-linker=sycl:spir64-unknown-unknown=-DFOO1 -DFOO2"
// CHK-TOOLS-OPTS2: clang-linker-wrapper{{.*}} "--device-linker=sycl:spir64-unknown-unknown=-DFOO1" "--device-linker=sycl:spir64-unknown-unknown=-DFOO2"

/// -fsycl-range-rounding settings
///
Expand Down
Loading
Loading