Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
16 commits
Select commit Hold shift + click to select a range
90fe7b6
[SYCL][NewOffloadModel] Enable -no-sycl-rdc in Clang Driver for NewOf…
maksimsab May 8, 2026
998d20f
fix test for cross-platform execution
maksimsab Jul 2, 2026
80d7d10
[SYCL][NewOffloadModel] Address review comments on PR #21973
srividya-sundaram Jul 30, 2026
dbd5c44
[SYCL][NewOffloadModel] Add -fno-sycl-rdc support at compile step (-c)
srividya-sundaram Jul 30, 2026
07a16e7
[SYCL][Driver] Fix tests for -fno-sycl-rdc compile-step support
srividya-sundaram Jul 30, 2026
2a41c6f
[SYCL] Fix clang-format issues
srividya-sundaram Jul 30, 2026
d8ece47
[SYCL] Address Copilot review comments on PR #22833
srividya-sundaram Jul 31, 2026
97ecb2f
[SYCL] Move no-RDC binary embedding logic to CGSYCLRuntime
srividya-sundaram Jul 31, 2026
7a9c782
[SYCL][Driver] Use getFinalPhase to guard --sycl-device-link in linke…
srividya-sundaram Jul 31, 2026
a45367c
[SYCL][Driver] Trim verbose comment in SYCL no-RDC host offload block
srividya-sundaram Aug 1, 2026
a2bc665
[SYCL][Driver] Add -fno-sycl-rdc -flto tests to sycl-no-rdc-new-driver
srividya-sundaram Aug 1, 2026
0e6ce2e
[SYCL][Driver] Trim verbose comment blocks in sycl-no-rdc-compile-ste…
srividya-sundaram Aug 1, 2026
187c79c
[SYCL][Driver] Use implicit-check-not for -fembed-offload-object in c…
srividya-sundaram Aug 1, 2026
fede3c5
[SYCL][Driver] Anchor -fsycl-include-target-binary check to host cc1 …
srividya-sundaram Aug 1, 2026
7746b37
[SYCL][Doc] Rework NoRDC section to describe new offload model usage
srividya-sundaram Aug 1, 2026
2be9203
[SYCL][Test] Pass -fno-sycl-rdc unconditionally in early_aot e2e test
srividya-sundaram Aug 1, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions clang/include/clang/Basic/CodeGenOptions.h
Original file line number Diff line number Diff line change
Expand Up @@ -401,6 +401,11 @@ class CodeGenOptions : public CodeGenOptionsBase {
/// CUDA runtime back-end for incorporating them into host-side object file.
std::string CudaGpuBinaryFileName;

/// Name of file passed with -fsycl-include-target-binary option. Used in
/// -fno-sycl-rdc mode to embed and register a per-TU finalized SYCL device
/// image into the host object at compile time.
std::string SYCLTargetBinaryFileName;

/// List of filenames passed in using the -fembed-offload-object option. These
/// are offloading binaries containing device images and metadata.
std::vector<std::string> OffloadObjects;
Expand Down
3 changes: 3 additions & 0 deletions clang/include/clang/Options/Options.td
Original file line number Diff line number Diff line change
Expand Up @@ -9469,6 +9469,9 @@ def fcuda_is_device : Flag<["-"], "fcuda-is-device">,
def fcuda_include_gpubinary : Separate<["-"], "fcuda-include-gpubinary">,
HelpText<"Incorporate CUDA device-side binary into host object file.">,
MarshallingInfoString<CodeGenOpts<"CudaGpuBinaryFileName">>;
def fsycl_include_target_binary : Separate<["-"], "fsycl-include-target-binary">,
HelpText<"Incorporate SYCL device-side binary into host object file.">,

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.

kind of a nit but i since the device binary will contain information on the offloading model and triple used (as it should be inside the OffloadBinary wrapper), i don't see any need for per-offload-model options, but since CUDA already did it, i guess we only can address this feedback when we implement an equivalent version of this change upstream, where we can unify them

@srividya-sundaram srividya-sundaram Jul 31, 2026

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 guess we only can address this feedback when we implement an equivalent version of this change upstream, where we can unify them

Agree.

This option was introduced based on the SYCL no-RDC design doc from @YuriPlyakhin : https://github.com/YuriPlyakhin/llvm-project/blob/6553d362c5e84114a30eff3a79cad70432fff3ef/sycl-no-rdc-plan.md#3-per-tu-device-finalize--host-compile-inclusion-cudahip-mirror

We can unify with the CUDA/HIP option when implementing the equivalent upstream change.

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.

Would it perhaps make sense to name the new option -foffload-include-binary or similar in anticipation of future unification?

MarshallingInfoString<CodeGenOpts<"SYCLTargetBinaryFileName">>;
def fcuda_allow_variadic_functions : Flag<["-"], "fcuda-allow-variadic-functions">,
HelpText<"Deprecated; Allow variadic functions in CUDA device code.">;
def fno_cuda_host_device_constexpr : Flag<["-"], "fno-cuda-host-device-constexpr">,
Expand Down
24 changes: 24 additions & 0 deletions clang/lib/CodeGen/CGSYCLRuntime.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,12 @@
#include "CodeGenFunction.h"
#include "clang/AST/Attr.h"
#include "clang/AST/Decl.h"
#include "clang/Basic/DiagnosticFrontend.h"
#include "clang/Basic/SourceLocation.h"
#include "llvm/Analysis/OptimizationRemarkEmitter.h"
#include "llvm/Frontend/Offloading/OffloadWrapper.h"
#include "llvm/IR/Instructions.h"
#include "llvm/Support/VirtualFileSystem.h"
#include <assert.h>

using namespace clang;
Expand Down Expand Up @@ -131,6 +134,27 @@ bool CGSYCLRuntime::actOnGlobalVarEmit(CodeGenModule &CGM, const VarDecl &D,
return true;
}

void clang::CodeGen::embedSYCLNoRDCBinary(CodeGenModule &CGM) {
if (!CGM.getLangOpts().SYCLIsHost ||
CGM.getCodeGenOpts().SYCLTargetBinaryFileName.empty())
return;
auto BinaryOrErr = CGM.getFileSystem()->getBufferForFile(
CGM.getCodeGenOpts().SYCLTargetBinaryFileName, /*MaxSize=*/-1,
/*RequiresNullTerminator=*/false);
if (std::error_code EC = BinaryOrErr.getError()) {
CGM.getDiags().Report(diag::err_cannot_open_file)
<< CGM.getCodeGenOpts().SYCLTargetBinaryFileName << EC.message();
return;
}
llvm::ArrayRef<char> Buffer((*BinaryOrErr)->getBufferStart(),
(*BinaryOrErr)->getBufferSize());
if (llvm::Error E = llvm::offloading::wrapSYCLBinaries(
CGM.getModule(), Buffer, llvm::offloading::SYCLJITOptions{}))
CGM.getDiags().Report(diag::err_fe_linking_module)
<< CGM.getCodeGenOpts().SYCLTargetBinaryFileName
<< llvm::toString(std::move(E));
}

bool Util::matchQualifiedTypeName(const CXXRecordDecl *RecTy,
ArrayRef<Util::DeclContextDesc> Scopes) {
// The idea: check the declaration context chain starting from the type
Expand Down
4 changes: 4 additions & 0 deletions clang/lib/CodeGen/CGSYCLRuntime.h
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,10 @@ class CGSYCLRuntime {
llvm::Value *Addr);
};

/// Embeds the per-TU finalized SYCL device binary into the host module when
/// -fsycl-include-target-binary is set (-fno-sycl-rdc compile step).
void embedSYCLNoRDCBinary(CodeGenModule &CGM);

} // namespace CodeGen
} // namespace clang

Expand Down
3 changes: 2 additions & 1 deletion clang/lib/CodeGen/CodeGenModule.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -50,9 +50,9 @@
#include "clang/CodeGen/BackendUtil.h"
#include "clang/CodeGen/ConstantInitBuilder.h"
#include "clang/Frontend/FrontendDiagnostic.h"
#include "clang/Lex/Preprocessor.h"
#include "clang/Sema/Sema.h"
#include "clang/Sema/SemaSYCL.h"
#include "clang/Lex/Preprocessor.h"
#include "llvm/ABI/IRTypeMapper.h"
#include "llvm/ABI/TargetInfo.h"
#include "llvm/ADT/STLExtras.h"
Expand Down Expand Up @@ -1257,6 +1257,7 @@ void CodeGenModule::Release() {
if (llvm::Function *CudaCtorFunction = CUDARuntime->finalizeModule())
AddGlobalCtor(CudaCtorFunction);
}
embedSYCLNoRDCBinary(*this);
if (OpenMPRuntime) {
OpenMPRuntime->createOffloadEntriesAndInfoMetadata();
OpenMPRuntime->clear();
Expand Down
21 changes: 19 additions & 2 deletions clang/lib/Driver/Driver.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -8339,9 +8339,26 @@ Driver::BuildOffloadingActions(Compilation &C, llvm::opt::DerivedArgList &Args,
DDep.add(*PackagerAction,
*C.getOffloadToolChains<Action::OFK_HIP>().first->second,
/*BA=*/{}, Action::OFK_HIP);
} else if (C.isOffloadingHostKind(Action::OFK_SYCL) &&
tools::SYCL::shouldDoPerObjectFileLinking(C) &&
!isa<LinkJobAction>(HostAction)) {
// SYCL -fno-sycl-rdc at compile time (-c): finalize this TU's device code
// immediately via clang-linker-wrapper --sycl-device-link --no-sycl-rdc,
// producing a self-contained device image. The image is passed to the host
// cc1 via -fsycl-include-target-binary and embedded+registered at compile
// time. The final link step does no SYCL device work. This mirrors the
// CUDA/HIP -fno-gpu-rdc per-TU finalize model; downstream uses
// clang-linker-wrapper instead of clang-sycl-linker.
Action *PackagerAction =
C.MakeAction<OffloadPackagerJobAction>(OffloadActions, types::TY_Image);
ActionList AL{PackagerAction};
Action *FinalizeAction =
C.MakeAction<LinkerWrapperJobAction>(AL, types::TY_Image);
DDep.add(*FinalizeAction, *C.getSingleOffloadToolChain<Action::OFK_Host>(),
/*BA=*/{}, Action::OFK_SYCL);
} else {
// Package all the offloading actions into a single output that can be
// embedded in the host and linked.
// RDC (default): package raw device bitcode to be embedded in the host
// object and device-linked across all TUs at final link time.
Action *PackagerAction =
C.MakeAction<OffloadPackagerJobAction>(OffloadActions, types::TY_Image);
DDep.add(*PackagerAction, *C.getSingleOffloadToolChain<Action::OFK_Host>(),
Expand Down
25 changes: 25 additions & 0 deletions clang/lib/Driver/ToolChains/Clang.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9149,10 +9149,17 @@ void Clang::ConstructJob(Compilation &C, const JobAction &JA,
CmdArgs.push_back(CudaDeviceInput->getFilename());
} else if (!HostOffloadingInputs.empty()) {
if ((IsCuda || IsHIP) && !IsRDCMode) {
// CUDA/HIP no-RDC: device image finalized per-TU, embed at compile time.

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.

since HIP already goes through this path, can we reuse the same -fcuda-include-gpubinary option so instead we could just change the if to if((IsCuda || isHIP || isSYCL) && !IsRDCMode)? this is kind of related to my comment before about the option

assert(HostOffloadingInputs.size() == 1 && "Only one input expected");
CmdArgs.push_back("-fcuda-include-gpubinary");
CmdArgs.push_back(HostOffloadingInputs.front().getFilename());
} else if (IsSYCL && !IsRDCMode) {
// SYCL no-RDC: per-TU finalized device image, embed at compile time.
assert(HostOffloadingInputs.size() == 1 && "One finalized image per TU");
CmdArgs.push_back("-fsycl-include-target-binary");
CmdArgs.push_back(HostOffloadingInputs.front().getFilename());
} else {
// RDC: embed raw device bitcode for cross-TU device link at link time.
for (const InputInfo Input : HostOffloadingInputs)
CmdArgs.push_back(Args.MakeArgString("-fembed-offload-object=" +
TC.getInputFilename(Input)));
Expand Down Expand Up @@ -12154,6 +12161,24 @@ void LinkerWrapper::ConstructJob(Compilation &C, const JobAction &JA,
if (Args.hasArg(options::OPT_fsycl_link_EQ))
CmdArgs.push_back(Args.MakeArgString("--sycl-device-link"));

// Propagate [no-]rdc mode to the linker wrapper for the SYCL case.
// The default behaviour is rdc mode ON, which requires no special flags.
// In order to enable non-rdc mode, we pass --no-sycl-rdc to the linker
// wrapper. Note: -f[no-]sycl-rdc is an alias of [no-]gpu_rdc.
bool IsSYCLNoRDC =
!Args.hasFlag(options::OPT_fgpu_rdc, options::OPT_fno_gpu_rdc,
/*default=*/true);
if (IsSYCLNoRDC) {
CmdArgs.push_back("--no-sycl-rdc");
// isDeviceOffloading(OFK_SYCL) distinguishes the per-TU finalizer
// (OFK_SYCL, from BuildOffloadingActions) from the final link-time
// invocation (OFK_Host) -- both produce TY_Image so output type alone
// is not a reliable discriminator.
if (C.getDriver().getFinalPhase(C.getArgs()) != phases::Link &&
JA.isDeviceOffloading(Action::OFK_SYCL))
CmdArgs.push_back("--sycl-device-link");
}

// -sycl-device-library-location=<dir> provides the location in which the
// SYCL device libraries can be found.
SmallString<128> DeviceLibDir(D.Dir);
Expand Down
29 changes: 29 additions & 0 deletions clang/test/Driver/sycl-no-rdc-compile-step.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
/// Tests for -fno-sycl-rdc at the compile step (-c) with --offload-new-driver.
/// Verifies that the driver inserts a per-TU clang-linker-wrapper finalize
/// action and routes the result to -fsycl-include-target-binary on the host
/// cc1, instead of deferring device processing to link time.

// RUN: touch %t.cpp

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.

I didn't catch this in the first RDC PR, but why do we need to do this? We should be able just to use %s in the tests.


// -fno-sycl-rdc -c: per-TU linker-wrapper with --sycl-device-link, host cc1 gets -fsycl-include-target-binary.
// RUN: %clang -### --offload-new-driver -Werror --target=x86_64-unknown-linux-gnu \
// RUN: -fsycl -fno-sycl-rdc -c %t.cpp 2>&1 \
// RUN: | FileCheck -check-prefix=CHK-COMPILE --implicit-check-not=-fembed-offload-object %s
// CHK-COMPILE: clang-linker-wrapper{{.*}} "--no-sycl-rdc"{{.*}} "--sycl-device-link"
// CHK-COMPILE: "-fsycl-is-host"{{.*}} "-fsycl-include-target-binary"

// Default RDC -c: -fembed-offload-object appears, no -fsycl-include-target-binary or --no-sycl-rdc.
// RUN: %clang -### --offload-new-driver --target=x86_64-unknown-linux-gnu \
// RUN: -fsycl -c %t.cpp 2>&1 \
// RUN: | FileCheck -check-prefix=CHK-RDC %s
// CHK-RDC: -fembed-offload-object
// CHK-RDC-NOT: -fsycl-include-target-binary
// CHK-RDC-NOT: --no-sycl-rdc

// -fno-sycl-rdc at link step: --no-sycl-rdc forwarded, --sycl-device-link must NOT appear.
// RUN: touch %t.o
// RUN: %clang -### --offload-new-driver -Werror --target=x86_64-unknown-linux-gnu \
// RUN: -fsycl -fno-sycl-rdc %t.o 2>&1 \
// RUN: | FileCheck -check-prefix=CHK-LINK %s
// CHK-LINK: clang-linker-wrapper{{.*}} "--no-sycl-rdc"
// CHK-LINK-NOT: clang-linker-wrapper{{.*}} "--sycl-device-link"
71 changes: 71 additions & 0 deletions clang/test/Driver/sycl-no-rdc-new-driver.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
/// Tests for -f[no-]sycl-rdc with --offload-new-driver.

// Verifies that --no-sycl-rdc is propagated to clang-linker-wrapper when
// -fno-sycl-rdc is passed. RDC is ON by default; --no-sycl-rdc signals
Comment thread
srividya-sundaram marked this conversation as resolved.
// RDC is OFF.

// RUN: touch %t.cpp

// Default (no flag): RDC is ON by default for SYCL, so --no-sycl-rdc should NOT appear.
// RUN: %clang -### --offload-new-driver --target=x86_64-unknown-linux-gnu -fsycl %t.cpp 2>&1 \
// RUN: | FileCheck -check-prefix=CHK-DEFAULT %s
// CHK-DEFAULT-NOT: --no-sycl-rdc

// -fno-sycl-rdc: --no-sycl-rdc should appear.
// RUN: %clang -### --offload-new-driver -Werror --target=x86_64-unknown-linux-gnu -fsycl -fno-sycl-rdc %t.cpp 2>&1 \
// RUN: | FileCheck -check-prefix=CHK-NO-RDC %s
// CHK-NO-RDC: clang-linker-wrapper{{.*}} "--no-sycl-rdc"

// AOT Intel GPU target, default RDC: --no-sycl-rdc should NOT appear.
// RUN: %clang -### --offload-new-driver --target=x86_64-unknown-linux-gnu -fsycl -fsycl-targets=intel_gpu_pvc %t.cpp 2>&1 \
// RUN: | FileCheck -check-prefix=CHK-AOT-RDC %s
// CHK-AOT-RDC-NOT: --no-sycl-rdc

// AOT Intel GPU target + -fno-sycl-rdc: --no-sycl-rdc should appear.
// RUN: %clang -### --offload-new-driver -Werror --target=x86_64-unknown-linux-gnu -fsycl -fsycl-targets=intel_gpu_pvc -fno-sycl-rdc %t.cpp 2>&1 \
// RUN: | FileCheck -check-prefix=CHK-AOT-NO-RDC %s
// CHK-AOT-NO-RDC: clang-linker-wrapper{{.*}} "--no-sycl-rdc"

// -fno-sycl-rdc -flto -c: per-TU device link still happens, --sycl-device-link present.
// RUN: %clang -### --offload-new-driver -Werror --target=x86_64-unknown-linux-gnu -fsycl -fno-sycl-rdc -flto -c %t.cpp 2>&1 \
// RUN: | FileCheck -check-prefix=CHK-NO-RDC-LTO-C %s
// CHK-NO-RDC-LTO-C: clang-linker-wrapper{{.*}} "--no-sycl-rdc"{{.*}} "--sycl-device-link"

// -fno-sycl-rdc -flto (link step): --sycl-device-link should NOT appear.
// RUN: %clang -### --offload-new-driver -Werror --target=x86_64-unknown-linux-gnu -fsycl -fno-sycl-rdc -flto %t.cpp 2>&1 \
// RUN: | FileCheck -check-prefix=CHK-NO-RDC-LTO %s
// CHK-NO-RDC-LTO: clang-linker-wrapper{{.*}} "--no-sycl-rdc"
// CHK-NO-RDC-LTO-NOT: --sycl-device-link

// Verify pipeline with --offload-new-driver -fno-sycl-rdc.
// RUN: touch %t1.cpp
// RUN: touch %t2.cpp
// RUN: %clang -### --offload-new-driver --target=x86_64-unknown-linux-gnu -fsycl -fno-sycl-rdc %t1.cpp %t2.cpp 2>&1 -ccc-print-phases | FileCheck %s --check-prefix=CHECK-PIPELINE

// CHECK-PIPELINE: 0: input, "{{.*}}1.cpp", c++, (host-sycl)
// CHECK-PIPELINE: 1: preprocessor, {0}, c++-cpp-output, (host-sycl)
// CHECK-PIPELINE: 2: compiler, {1}, ir, (host-sycl)
// CHECK-PIPELINE: 3: input, "{{.*}}1.cpp", c++, (device-sycl)
// CHECK-PIPELINE: 4: preprocessor, {3}, c++-cpp-output, (device-sycl)
// CHECK-PIPELINE: 5: compiler, {4}, ir, (device-sycl)
// CHECK-PIPELINE: 6: backend, {5}, ir, (device-sycl)
// CHECK-PIPELINE: 7: offload, "device-sycl (spir64-unknown-unknown)" {6}, ir
// CHECK-PIPELINE: 8: llvm-offload-binary, {7}, image, (device-sycl)
// CHECK-PIPELINE: 9: clang-linker-wrapper, {8}, image, (device-sycl)
// CHECK-PIPELINE: 10: offload, "host-sycl (x86_64-unknown-linux-gnu)" {2}, "device-sycl (x86_64-unknown-linux-gnu)" {9}, ir
// CHECK-PIPELINE: 11: backend, {10}, assembler, (host-sycl)
// CHECK-PIPELINE: 12: assembler, {11}, object, (host-sycl)
// CHECK-PIPELINE: 13: input, "{{.*}}2.cpp", c++, (host-sycl)
// CHECK-PIPELINE: 14: preprocessor, {13}, c++-cpp-output, (host-sycl)
// CHECK-PIPELINE: 15: compiler, {14}, ir, (host-sycl)
// CHECK-PIPELINE: 16: input, "{{.*}}2.cpp", c++, (device-sycl)
// CHECK-PIPELINE: 17: preprocessor, {16}, c++-cpp-output, (device-sycl)
// CHECK-PIPELINE: 18: compiler, {17}, ir, (device-sycl)
// CHECK-PIPELINE: 19: backend, {18}, ir, (device-sycl)
// CHECK-PIPELINE: 20: offload, "device-sycl (spir64-unknown-unknown)" {19}, ir
// CHECK-PIPELINE: 21: llvm-offload-binary, {20}, image, (device-sycl)
// CHECK-PIPELINE: 22: clang-linker-wrapper, {21}, image, (device-sycl)
// CHECK-PIPELINE: 23: offload, "host-sycl (x86_64-unknown-linux-gnu)" {15}, "device-sycl (x86_64-unknown-linux-gnu)" {22}, ir
// CHECK-PIPELINE: 24: backend, {23}, assembler, (host-sycl)
// CHECK-PIPELINE: 25: assembler, {24}, object, (host-sycl)
// CHECK-PIPELINE: 26: clang-linker-wrapper, {12, 25}, image, (host-sycl)
2 changes: 2 additions & 0 deletions sycl/doc/design/NonRelocatableDeviceCode.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
# Non-Relocatable Device Code

Note: This document reflects the design of NoRDC Mode support in the Old Offload Model.

## Overview
By default, SYCL allows device code to be relocatable, where function calls outside
of the current translation unit are allowed using the `SYCL_EXTERNAL` attribute.
Expand Down
25 changes: 25 additions & 0 deletions sycl/doc/design/OffloadDesign.md
Original file line number Diff line number Diff line change
Expand Up @@ -243,6 +243,31 @@ are needed to pass along this information.

*Table: Ahead of Time Info*

### NoRDC Mode support

For the Old Offload Model support of NoRDC Mode see [NonRelocatableDeviceCode.md](NonRelocatableDeviceCode.md).
Comment thread
srividya-sundaram marked this conversation as resolved.

The default compiler behavior is `-fsycl-rdc`, which incorporates linking of device code.
If `-fno-sycl-rdc` is specified, the compiler skips linking of device code and performs
offload processing on every module individually.

With the new offload model, `-fno-sycl-rdc` can be specified at the link step:
```
clang++ --offload-new-driver -fsycl -fsycl-targets=T1,T2 input1.cpp -c -o object1.o
clang++ --offload-new-driver -fsycl -fsycl-targets=T1,T2 input2.cpp -c -o object2.o
clang++ --offload-new-driver -fsycl -fsycl-targets=T1,T2 -fno-sycl-rdc object1.o object2.o -o a.out
```

It can also be specified at the compile step:
```
clang++ --offload-new-driver -fsycl -fsycl-targets=T1,T2 -fno-sycl-rdc input1.cpp -c -o object1.o
clang++ --offload-new-driver -fsycl -fsycl-targets=T1,T2 -fno-sycl-rdc input2.cpp -c -o object2.o
clang++ --offload-new-driver -fsycl -fsycl-targets=T1,T2 object1.o object2.o -o a.out
```
In this case `clang-linker-wrapper --sycl-device-link --no-sycl-rdc` is invoked per
translation unit at compile time to finalize each TU's device code independently,
embedding the result directly into the host object.

#### Format of the --device-compiler Option
The `--device-compiler` option uses the format `--device-compiler=[<kind>:][<triple>=]<value>` where:
- `<kind>` : specifies the offloading kind (e.g., sycl, hip, openmp) and is optional.
Expand Down
8 changes: 5 additions & 3 deletions sycl/test-e2e/AOT/early_aot.cpp
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
// Test early-AOT behaviors with -fsycl -fno-sycl-rdc. This targets spir64_gen

// REQUIRES: ocloc, gpu, target-spir
// XFAIL: new-offload-model
// XFAIL-TRACKER: CMPLRLLVM-51875

// Note: For early AOT, -fno-sycl-rdc must be specified at the compilation step
// for the Old Offload Model. The New Offload Model supports -fno-sycl-rdc at
// both the compile and link steps.

// Build the early AOT device binaries
// RUN: %clangxx -fsycl -fsycl-targets=spir64_gen -Xsycl-target-backend=spir64_gen %gpu_aot_target_opts -fno-sycl-rdc -c -DADD_CPP %s -o %t_add.o
// RUN: %clangxx -fsycl -fsycl-targets=spir64_gen -Xsycl-target-backend=spir64_gen %gpu_aot_target_opts -fno-sycl-rdc -c -DSUB_CPP %s -o %t_sub.o
// RUN: %clangxx -fsycl -DMAIN_CPP %s %t_add.o %t_sub.o -o %t.out
// RUN: %clangxx -fsycl -fno-sycl-rdc -DMAIN_CPP %s %t_add.o %t_sub.o -o %t.out

// RUN: %{run} %t.out

Expand Down