[SYCL][NewOffloadModel] Support -fno-sycl-rdc at compile step (-c) - #22833
[SYCL][NewOffloadModel] Support -fno-sycl-rdc at compile step (-c)#22833srividya-sundaram wants to merge 41 commits into
Conversation
Fix --no-offload-old-driver typo to --no-offload-new-driver in the old offload model usage example. Add a note that compile-step support for -fno-sycl-rdc (matching the old model's -c usage pattern) will be implemented in a follow-up patch via clang-linker-wrapper --sycl-device-link --no-sycl-rdc per translation unit.
Adds support for specifying -fno-sycl-rdc at the compile step in the new offload model, matching the old offload model's usage pattern. Previously, -fno-sycl-rdc was only meaningful at the link step in the new model. Design (mirrors CUDA/HIP -fno-gpu-rdc per-TU finalize model): - BuildOffloadingActions: when -fno-sycl-rdc is passed at compile time, insert a per-TU LinkerWrapperJobAction (OffloadPackager + clang-linker-wrapper --sycl-device-link --no-sycl-rdc) to finalize the TU's device code immediately, producing a self-contained device image. - Clang::ConstructJob: route the finalized image to the new -fsycl-include-target-binary CC1 option instead of -fembed-offload-object, so the host CodeGen embeds and registers the image at compile time rather than deferring it to link time. - CodeGenModule::Release: when -fsycl-include-target-binary is set on the host cc1, read the device image file and call wrapSYCLBinaries to embed the binary and emit __sycl_register_lib/__sycl_unregister_lib ctors into the host module. The final clang-linker-wrapper invocation then finds no SYCL device input and performs a plain host link. - Downstream uses clang-linker-wrapper --sycl-device-link instead of the upstream's clang-sycl-linker, as all Intel SYCL pipeline logic resides in clang-linker-wrapper. New files/options: - -fsycl-include-target-binary <file>: new CC1 option (analog of -fcuda-include-gpubinary) marshalled into CodeGenOpts.SYCLTargetBinaryFileName - clang/test/Driver/sycl-no-rdc-compile-step.cpp: driver tests verifying per-TU pipeline shape, RDC default regression, and link-step regression. Implements compile-step support tracked in CMPLRLLVM-51875. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
There was a problem hiding this comment.
Pull request overview
Adds compile-step (-c) support for -fno-sycl-rdc in the new SYCL offload model by finalizing each TU’s device image during compilation (via a per-TU clang-linker-wrapper --sycl-device-link --no-sycl-rdc) and embedding/registering the finalized image into the host object using a new cc1 option.
Changes:
- Teach the driver to insert a per-TU
LinkerWrapperJobActionfor SYCL NoRDC compilation and route its output to the host compile. - Introduce
-fsycl-include-target-binaryand plumb it through driver → CodeGen to embed/register the finalized SYCL image at compile time. - Add/update driver + e2e tests and update design documentation around NoRDC usage patterns.
Reviewed changes
Copilot reviewed 10 out of 10 changed files in this pull request and generated 5 comments.
Show a summary per file
| File | Description |
|---|---|
| sycl/test-e2e/AOT/early_aot.cpp | Adjust RUN lines for old/new model flag placement; adds explanatory note. |
| sycl/doc/design/OffloadDesign.md | Document NoRDC behavior and usage scenarios for old/new offload models. |
| sycl/doc/design/NonRelocatableDeviceCode.md | Clarify document scope (old offload model NoRDC design). |
| clang/test/Driver/sycl-no-rdc-new-driver.cpp | New driver test for -fno-sycl-rdc propagation and pipeline shape. |
| clang/test/Driver/sycl-no-rdc-compile-step.cpp | New driver test covering compile-step NoRDC finalization + cc1 plumbing. |
| clang/lib/Driver/ToolChains/Clang.cpp | Pass finalized SYCL image to host cc1; propagate NoRDC to linker wrapper and add per-TU device-link mode logic. |
| clang/lib/Driver/Driver.cpp | Insert per-TU linker-wrapper finalizer action for SYCL NoRDC during compilation. |
| clang/lib/CodeGen/CodeGenModule.cpp | Read finalized SYCL image and call wrapSYCLBinaries to embed/register at compile time. |
| clang/include/clang/Options/Options.td | Add new -fsycl-include-target-binary option (marshalled to CodeGenOpts). |
| clang/include/clang/Basic/CodeGenOptions.h | Add SYCLTargetBinaryFileName storage for the new cc1 option. |
|
SYCL Pre commit failure is unrelated to the changes in this PR. |
…Offload Model (#22832) ## Summary This patch adds `-fno-sycl-rdc` support to the new SYCL offload model in the Clang driver. It is based on the work by @maksimsab in #21973, with review fixes applied. ### What this patch does By default (`-fsycl-rdc`), all device code across translation units is linked together into one module at link time before post-link processing. With `-fno-sycl-rdc`, each object file's device code is processed independently — skipping the `llvm-link` step — which can significantly reduce peak memory and compile time. **Changes:** - `clang/lib/Driver/ToolChains/Clang.cpp`: In `LinkerWrapper::ConstructJob`, propagate `--no-sycl-rdc` to `clang-linker-wrapper` when `-fno-sycl-rdc` is passed at link time. - `sycl/doc/design/OffloadDesign.md`: Document NoRDC mode for the new offload model. - `sycl/doc/design/NonRelocatableDeviceCode.md`: Add a note clarifying that document covers the old offload model only. - `sycl/test-e2e/AOT/early_aot.cpp`: Un-XFAIL the test for the new offload model; adjust RUN lines to pass `-fno-sycl-rdc` at the correct step per model. - `clang/test/Driver/sycl-no-rdc-new-driver.cpp`: New driver test verifying `--no-sycl-rdc` is propagated to `clang-linker-wrapper` correctly. ### Key behavioral difference from the old offload model | Model | Where to pass `-fno-sycl-rdc` | |---|---| | Old (`--no-offload-new-driver`) | At each **compile** step (`-c`) | | New (`--offload-new-driver`) | At the **link** step | This inversion exists because in the new model all SYCL offload processing (post-link, AOT, wrapping) lives in `clang-linker-wrapper` at link time. ### Follow-up work Supporting `-fno-sycl-rdc -c` in the new model (matching the old model's compile-step ergonomics) will be addressed in a follow-up patch. The plan is to invoke `clang-linker-wrapper --sycl-device-link --no-sycl-rdc` per translation unit at compile time, embedding the finalized device image directly into the host object. Supporting -fno-sycl-rdc -c in the new model (matching old model compile-step ergonomics) is addressed in #22833. Fixes: CMPLRLLVM-51875 🤖 Generated with [Claude Code](https://claude.com/claude-code) --------- Co-authored-by: Sabianin, Maksim <maksim.sabianin@intel.com> Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
Move embedSYCLNoRDCBinary out of CodeGenModule::Release() into a free function in CGSYCLRuntime.cpp to keep SYCL-specific logic out of common code. CodeGenModule::Release() retains a single call site. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
KseniyaTikhomirova
left a comment
There was a problem hiding this comment.
SYCL RT changes LGTM
…ile, remove fsycl-include-target-binary alias, use proper DK_Linker forwarding handler
tahonermann
left a comment
There was a problem hiding this comment.
I'm not yet convinced that the diagnostic workaround is needed. Please see the inline comment.
…fix embedSYCLNoRDCBinary ordering, update OffloadDesign.md wording
…rupted module after no-RDC bitcode link failure
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 22 out of 22 changed files in this pull request and generated 1 comment.
Suppressed comments (1)
sycl/test-e2e/AOT/early_aot.cpp:8
- The "Old offload model" RUN lines don’t explicitly force the old model (e.g. via
--no-offload-new-driver). In configurations where the new offload model is enabled by default, these commands would exercise the new model instead, reducing coverage and making the section header misleading.
// Old offload model: -fno-sycl-rdc at compile step only.
// 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
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…mbedSYCLNoRDCBinary
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 21 out of 21 changed files in this pull request and generated 3 comments.
Suppressed comments (3)
clang/include/clang/Options/Options.td:1931
- The PR description specifies a new
-fsycl-include-target-binaryCC1 option, but this change defines only-foffload-include-binary(and aliases the CUDA spelling to it). As written, the promised SYCL option is not available; either implement the described spelling or update the PR contract and all documentation/tests consistently.
def foffload_include_binary : Separate<["-"], "foffload-include-binary">,
Visibility<[CC1Option]>,
HelpText<"Incorporate offloading device-side binary into host object file.">,
MarshallingInfoString<CodeGenOpts<"OffloadBinaryToEmbedFile">>;
clang/lib/Driver/Driver.cpp:8390
- The
!isa<LinkJobAction>condition also selects preprocessing and syntax-only host actions, even though this finalizer is intended for compile steps that produce a host object/IR. For-fsycl -fno-sycl-rdc -Ewithout-o(the special preprocessing branch above only handles an explicit output), this adds an image linker-wrapper to a preprocessing-only pipeline whose device actions have no linkable image. Restrict this branch to host actions with a real compilable output and leave preprocessing/syntax-only handling on their existing paths.
} else if (C.isOffloadingHostKind(Action::OFK_SYCL) &&
tools::SYCL::shouldDoPerObjectFileLinking(C) &&
!isa<LinkJobAction>(HostAction)) {
clang/lib/Driver/ToolChains/Clang.cpp:12165
getFinalPhasedescribes the whole driver invocation, not this nested per-TU action. For a compile-and-link command such asclang++ --offload-new-driver -fsycl -fno-sycl-rdc a.cpp -o a.out, the action graph still creates thisOFK_SYCLfinalizer (as the new pipeline check demonstrates), but the final phase isLink, so both flags are suppressed. The wrapper then takes the normalrunWrapperAndCompilepath and returns an object, while the host cc1 is given-foffload-include-binaryand tries to parse that file as wrapper bitcode. Forward the flags based on the per-TU action rather than the global final phase, and update the link-step test to cover this distinction.
if (C.getDriver().getFinalPhase(C.getArgs()) != phases::Link &&
JA.isDeviceOffloading(Action::OFK_SYCL)) {
tahonermann
left a comment
There was a problem hiding this comment.
Thank you for working with me through the link module control flow changes this week @srividya-sundaram! This looks much cleaner to me now.
I added a couple of comments to note minor fixes.
Just something I want to note to be aware of. With these changes, the no-rdc SYCL device image (if specified) will be linked in everywhere loadLinkModules() is called (which currently includes CodeGenAction::CreateASTConsumer(), CodeGenAction::loadModule(), and CIRGenAction::BeginSourceFileAction()). I think that is a good thing, but it isn't how the modules returned from getBitcodeModuleList() (a list populated by, I think, -mlink-bitcode-file) are handled, so there might be some subtle reason to do things differently. I don't recommend making a change unless/until there is a demonstrable reason to do so.
It looks like there might be some interactions with -flto=thin that might warrant some additional tests. In CodeGenAction::loadModule(), if CI.getCodeGenOpts().ThinLTOIndexFile.empty() is false, the loadLinkModules() call won't be reached. I don't know if that is a good thing or otherwise.
|
Thank you for the helpful offline discussions, @tahonermann!
It appears that loadModule is called only when the input is IR |
tahonermann
left a comment
There was a problem hiding this comment.
Thanks, @srividya-sundaram, the updates look great!
Summary
This PR adds support for
-fno-sycl-rdc -cin the new offload model by finalizing each TU's device code at compile time viaclang-linker-wrapper --sycl-device-link --no-sycl-rdc, then embedding the result into the host object via a new-fsycl-include-target-binaryCC1option. The approach mirrors how CUDA/HIP handle no-RDC with-fcuda-include-gpubinary.Depends on #22832.