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.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Can you elaborate on what exactly this RUN line is supposed to check?
When I read original comment, I assumed that the goal of this test case is to verify that clang-linker-wrapper does not set some sycl-post-link argument. If so, I don't think this line does that.
sycl-post-link{{.*}} matches any arguments.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

I see that the test is against -o option and line 122 checks "intel_gpu_pvc," prefix.

// CHK-CMDS-AOT-GEN-NEXT: sycl-post-link{{.*}} SYCL_POST_LINK_OPTIONS -o intel_gpu_pvc,[[SYCLPOSTLINKOUT:.*]].table [[SECONDLLVMLINKOUT]].bc

So, the RUN line checks that -o value doesn't have comas to exclude this pattern. Did I get it right?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

// 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.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

// 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
Comment thread
bader marked this conversation as resolved.
// 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