Skip to content

[Clang][OffloadBundler] Match the target ID of SYCL bundle entry IDs - #23491

Open
srividya-sundaram wants to merge 3 commits into
intel:syclfrom
srividya-sundaram:sycl-bundler-sycl-target-id
Open

srividya-sundaram wants to merge 3 commits into
intel:syclfrom
srividya-sundaram:sycl-bundler-sycl-target-id

Conversation

@srividya-sundaram

Copy link
Copy Markdown
Contributor

Fixes #23487.

With several intel_gpu_* (or nvidia_gpu_*) targets and separate compile and link, clang-offload-bundler returned the first architecture's device code for every architecture, because the architecture in SYCL bundle IDs like sycl-spir64_gen-unknown-unknown-pvc was parsed as the triple environment.

The architecture is now parsed as the target ID. A SYCL bundle without an architecture still matches any architecture of the same triple, so existing objects keep working.

Split out of #23441, whose tests need it.

SYCL bundle entry IDs use a 3-component triple followed by the target ID,
e.g. sycl-spir64_gen-unknown-unknown-pvc. The bundler parsed the target ID
as the triple environment and left the target ID empty, so all entries with
the same triple matched each other. With several -fsycl-targets=intel_gpu_*
(or nvidia_gpu_*) targets and separate compile and link in the old
offloading model, every target was then unbundled to the first entry's
device code.

Take the last component as the target ID when it is not a known environment
but is a known offload architecture. A SYCL code object without a target ID
stays compatible with any target ID of the same triple, as before.

@iclsrc iclsrc left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

I found no critical issues. The PR teaches OffloadTargetInfo to parse SYCL bundle entry IDs of the form sycl-<arch>-<vendor>-<os>-<targetid>. It also adds an isCodeObjectCompatible rule that treats SYCL code objects with no target ID as compatible with any target ID of the same triple. The parsing change is narrow: it requires 5 components, an unknown environment, and a recognized offload arch as the last component. The 4-component and 6-component forms are unaffected. I have one test-coverage comment below.

Comment thread clang/lib/Driver/OffloadBundler.cpp

Copilot AI left a comment

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.

Warning

Copilot couldn't run its full agentic review because it didn't start before the timeout. Make sure your repository has a runner available, or add a copilot-code-review.yml file specifying one with the runs-on attribute. See the docs for more details.

Copilot review overview

2 open findings
What changed in this PR

Fixes clang-offload-bundler target matching for SYCL bundle entry IDs by treating the final component (e.g. pvc) as a SYCL target ID rather than the triple environment, ensuring the correct device code is selected per architecture.

Changes:

  • Update OffloadTargetInfo parsing to recognize 3-component SYCL triples followed by a target ID.
  • Relax SYCL compatibility rules so bundles without a target ID still match bundles/targets of the same triple.
  • Add a regression test covering multi-architecture SYCL (including NVPTX and “gen_image”) unbundling behavior.
File Description
clang/​test/​OffloadTools/​clang-offload-bundler/​sycl-target-id.c Adds coverage to ensure each SYCL target ID unbundles to the correct code object and preserves compatibility for “no target ID” bundles.
clang/​lib/​Driver/​OffloadBundler.cpp Adjusts parsing of SYCL bundle IDs and compatibility logic to use target ID matching instead of misinterpreting it as triple environment.

🧠 Review effort: Lite


💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread clang/lib/Driver/OffloadBundler.cpp Outdated
Comment thread clang/test/OffloadTools/clang-offload-bundler/sycl-target-id.c
Only treat 5-component bundle IDs as triple plus target ID for SYCL.
@srividya-sundaram
srividya-sundaram marked this pull request as ready for review October 15, 2026 01:51
@srividya-sundaram
srividya-sundaram requested a review from a team as a code owner October 15, 2026 01:51
// e.g. sycl-spir64_gen-unknown-unknown-pvc, so the last component is the
// target ID rather than the environment.
if (this->OffloadKind == "sycl" && Components.size() == 5 &&
T.getEnvironment() == llvm::Triple::UnknownEnvironment &&

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.

just wondering, why the need for the environment check? could we have a 5 size triple with a non-empty env that passes the StringToOffloadArch check?

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.

could we have a 5 size triple with a non-empty env that passes the StringToOffloadArch check?

If you are asking if an env name could also be an arch name currently, then no.

But a 5-part SYCL ID can end in either an env or an arch

sycl-x86_64-unknown-linux-gnu          // 5th part is an env (gnu)
sycl-spir64_gen-unknown-unknown-pvc    // 5th part is an arch (pvc)

The env check here verifies that the 5th part is not a valid env name.
If not an env, we check if its a valid arch. The env check is more of a safety net, not strictly needed given that no env == arch name

ArrayRef<StringRef> TripleSlice{&Components[1], /*length=*/4};
llvm::Triple T = llvm::Triple(llvm::join(TripleSlice, "-"));

// SYCL bundle entry IDs use a 3-component triple followed by the target ID,

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.

more of a general question, it seems from the test added here that the amd componnts passed to this tool are like hipv4-amdgcn-amd-amdhsa--gfx908, so it seems they dont pass the offloading type. if that's accurate, do you know why we need the offloading type but they dont?

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.

more of a general question, it seems from the test added here that the amd componnts passed to this tool are like hipv4-amdgcn-amd-amdhsa--gfx908, so it seems they dont pass the offloading type.

hmm not sure I follow your question.
hipv4 is the offload kind here.
The only difference is SYCL uses a 3 part triple (arch-vendor-os) while HIP uses a 4 part triple(arch-vendor-os-env)
ex:
sycl-spir64_gen-unknown-unknown-pvc // arch is the 5th part, where the env would be
So for SYCL, the bundler read pvc as the environment. That's what this PR fixes.

// target ID rather than the environment.
if (this->OffloadKind == "sycl" && Components.size() == 5 &&
T.getEnvironment() == llvm::Triple::UnknownEnvironment &&
!clang::StringToOffloadArch(Components.back().split(':').first)

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.

what is Components.back() here? give it's a 5 size vector, i would have expected it would just be pvc, but it seems there's a : somewhere

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.

what is Components.back() here?

It's the target ID, which might include features after a colon.
The colon is stripped for the arch lookup:

ex:

sycl-amdgcn-amd-amdhsa-gfx90a:xnack+
  Components.back()                  --> gfx90a:xnack+
  Components.back().split(':').first --> gfx90a

sycl-spir64_gen-unknown-unknown-pvc
  Components.back()                  --> pvc
  Components.back().split(':').first --> pvc

{
Expected<std::unique_ptr<FileHandler>> IDHandlerOrErr =
CreateFileHandler(Input, BundlerConfig);
if (!IDHandlerOrErr)

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.

same question here on the triple check

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.

There is no triple check here.

For ex:

clang++ -fsycl --no-offload-new-driver -fsycl-targets=spir64_gen,intel_gpu_pvc 
-c t.cpp -o a.o

clang-offload-bundler -type=o -unbundle -input=a.o -output=pvc.o 
-targets=sycl-spir64_gen-unknown-unknown-pvc

The requested ID is sycl-spir64_gen-unknown-unknown-pvc (from -targets).
a.o holds two device-code sections, in this order:

sycl-spir64_gen-unknown-unknown: from spir64_gen, which has no target ID
sycl-spir64_gen-unknown-unknown-pvc: from intel_gpu_pvc, target ID pvc

Before: the bundler reads the sections in order and stops at the first one compatible with the requested ID. Section 1 is compatible, so pvc.o got section 1, not section 2.

After: this block first lists all of a.o's sections, so the bundler sees that section 2 exactly matches the requested ID and extracts it. Section 1 is only used when no section has the requested target ID.

// SYCL device code without a target ID (e.g. -fsycl-targets=spir64_gen)
// stays compatible with any target ID of the same triple.
if (CodeObjectInfo.OffloadKind == "sycl" &&
(CodeObjectInfo.TargetID.empty() || TargetInfo.TargetID.empty())) {

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.

do we need to check for spir64_gen here? what if it's sycl offloading with two nvptx images and one of them has no targetid?

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.

what if it's sycl offloading with two nvptx images and one of them has no targetid?

I don't think we should restrict it to spir64_gen triple. nvptx triples have the ID-less entries too.
Tried this locally:

# nvptx object whose bundle entry has no arch label
clang++ -fsycl --no-offload-new-driver -nocudalib -fsycl-targets=nvptx64-nvidia-cuda
-fno-bundle-offload-arch -c t.cpp -o a.o

# nvptx object whose bundle entry is sm_80
clang++ -fsycl --no-offload-new-driver -nocudalib -fsycl-targets=nvidia_gpu_sm_80 -c u.cpp -o b.o

# merge them, so one bundle holds both entries
ld -r a.o b.o -o c.o

clang-offload-bundler -list -type=o -input=c.o
sycl-nvptx64-nvidia-cuda          # no arch (a.o)
host-x86_64-unknown-linux-gnu
sycl-nvptx64-nvidia-cuda-sm_80    # sm_80 (b.o)

# unbundle each arch and compare with a.o's / b.o's own device code
clang-offload-bundler -type=o -input=c.o -unbundle -output=out -targets=sycl-nvptx64-nvidia-cuda-<arch>

Unbundling sm_80 returns the b.o's sm_80 code
Unbundling sm_90: returns a.o's ID-less code, since there is no sm_90 entry

Before this PR, sm_80 was parsed as the triple environment, so both entries matched and the first one won.
Now an exact target-ID match is always preferred, and the ID-less entry is used only when there is no exact one. Limiting the rule to spir64_gen would make the sm_90 case find nothing for ID-less nvptx objects.

Comment thread clang/lib/Driver/OffloadBundler.cpp

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[SYCL] clang-offload-bundler returns the first architecture's device code for every intel_gpu_* target

4 participants