[Driver] LinkerWrapper::ConstructJob builds a device link job only to overwrite it in the fat-binary case
- Dominant language
- LLVM
- Stars
- 40.5k
- Forks
- 18.7k
- PR merge metrics
- PR metrics pending
Description
`LinkerWrapper::ConstructJob` asks the device toolchain to build a link job
https://github.com/llvm/llvm-project/blob/f33ef3a26db25f9a4b75a25af6527304bc03fcb8/clang/lib/Driver/ToolChains/Clang.cpp#L10032-L10034
Then overwrites it in place:
https://github.com/llvm/llvm-project/blob/f33ef3a26db25f9a4b75a25af6527304bc03fcb8/clang/lib/Driver/ToolChains/Clang.cpp#L10142-L10145
For `TY_HIP_FATBIN` nothing from that job survives — `CmdArgs` is built from scratch and the job's own arguments are dropped
https://github.com/llvm/llvm-project/blob/f33ef3a26db25f9a4b75a25af6527304bc03fcb8/clang/lib/Driver/ToolChains/Clang.cpp#L10110-L10118
The only thing read out of it is `LinkCommand->getExecutable()`, for `--linker-path=`
https://github.com/llvm/llvm-project/blob/f33ef3a26db25f9a4b75a25af6527304bc03fcb8/clang/lib/Driver/ToolChains/Clang.cpp#L10101-L10103
and that option is unused in fat-binary case: its only two readers in the wrapper are `runLinker`, which `--emit-fatbin-only` skips
https://github.com/llvm/llvm-project/blob/f33ef3a26db25f9a4b75a25af6527304bc03fcb8/clang/tools/clang-linker-wrapper/ClangLinkerWrapper.cpp#L1639-L1640
and `linkerSupportsLTO`, which consults it only in the `!Triple.isGPU()` arm
https://github.com/llvm/llvm-project/blob/f33ef3a26db25f9a4b75a25af6527304bc03fcb8/clang/tools/clang-linker-wrapper/ClangLinkerWrapper.cpp#L296-L301
It is not forwarded to any sub-tool.
Hence, the wrapper is handed a linker path it never uses:
$ clang -### --target=x86_64-unknown-linux-gnu -x hip --offload-arch=gfx90a -fno-gpu-rdc -nogpulib -nogpuinc -c h.hip -o h.o
...clang-linker-wrapper" ... "--linker-path=.../bin/clang-offload-bundler" "--emit-fatbin-only" ...
The contract also obliges every device toolchain reaching this path to produce a command that is going to be discarded, whether or not it has anything meaningful to emit.
Proposed fix:
skip `Linker->ConstructJob` when `JA.getType() != types::TY_Image` and let `LinkerWrapper::ConstructJob` add its own command — it already has `Exec` and a complete `CmdArgs` at that point — pushing `--linker-path=` only in the `TY_Image` branch:
```c++
Command *LinkCommand = nullptr;
...
if (JA.getType() == types::TY_Image) {
Linker->ConstructJob(C, JA, Output, Inputs, Args, LinkingOutput);
LinkCommand = C.getJobs().getJobs().back().get();
CmdArgs.push_back(Args.MakeArgString(Twine("--linker-path=") +
LinkCommand->getExecutable()));
...
}
...
if (LinkCommand) {
LinkCommand->replaceExecutable(Exec);
LinkCommand->replaceArguments(CmdArgs);
} else {
C.addCommand(std::make_unique(JA, *this, ResponseFileSupport::None(),
Exec, CmdArgs, Inputs, Output));
}
```
Contributor guide
Research direction
Start in clang/lib/Driver/ToolChains/Clang.cpp at LinkerWrapper::ConstructJob, then inspect clang/tools/clang-linker-wrapper/ClangLinkerWrapper.cpp at runLinker and linkerSupportsLTO. Reproduce the supplied clang -### HIP command and verify that non-TY_Image jobs avoid constructing the discarded device link job while image jobs still provide --linker-path=.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- cpp
- Domain
- compilers
- Issue type
- Refactor
- Difficulty
- 3/5
- Estimated time
- 1-2 days
- Activity status
- Active
- Clarity
- Clearly specified
- Newbie friendliness
- 74/100