Repository navigation
[Clang][OffloadBundler] Match the target ID of SYCL bundle entry IDs - #23491
srividya-sundaram wants to merge 3 commits into
Conversation
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
left a comment
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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
OffloadTargetInfoparsing 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.
Only treat 5-component bundle IDs as triple plus target ID for SYCL.
| // 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 && |
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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, |
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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) |
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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) |
There was a problem hiding this comment.
same question here on the triple check
There was a problem hiding this comment.
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())) { |
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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.


Fixes #23487.
With several
intel_gpu_*(ornvidia_gpu_*) targets and separate compile and link,clang-offload-bundlerreturned the first architecture's device code for every architecture, because the architecture in SYCL bundle IDs likesycl-spir64_gen-unknown-unknown-pvcwas 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.