[clang] 2983555 - [HIP] Support device-only linking of bitcode offload binaries (#212342)
via cfe-commits
cfe-commits at lists.llvm.org
Tue Jul 28 07:19:36 PDT 2026
Author: Yaxun (Sam) Liu
Date: 2026-07-28T10:19:31-04:00
New Revision: 298355525c28343fecdee28b0cf75c8d94e96952
URL: https://github.com/llvm/llvm-project/commit/298355525c28343fecdee28b0cf75c8d94e96952
DIFF: https://github.com/llvm/llvm-project/commit/298355525c28343fecdee28b0cf75c8d94e96952.diff
LOG: [HIP] Support device-only linking of bitcode offload binaries (#212342)
The new offload driver packages multi-architecture bitcode in LLVM
offload binaries. A later `--hip-link --offload-device-only`
invocation treated these `.bc` inputs as host LLVM IR and dropped
them before linking.
Pass these inputs directly to clang-linker-wrapper when producing a
device fat binary. The wrapper can then extract and link each
requested GPU architecture.
Added:
clang/test/Driver/hip-link-offload-binary.c
Modified:
clang/lib/Driver/Driver.cpp
clang/lib/Driver/ToolChains/Clang.cpp
Removed:
################################################################################
diff --git a/clang/lib/Driver/Driver.cpp b/clang/lib/Driver/Driver.cpp
index 2ef9ffe0b9426..38795f7c2ae7a 100644
--- a/clang/lib/Driver/Driver.cpp
+++ b/clang/lib/Driver/Driver.cpp
@@ -4542,6 +4542,14 @@ void Driver::BuildActions(Compilation &C, DerivedArgList &Args,
bool UseNewOffloadingDriver = Args.hasFlag(
options::OPT_offload_new_driver, options::OPT_no_offload_new_driver,
C.getActiveOffloadKinds() != Action::OFK_None);
+ bool HIPRDCDeviceOnlyFatBin =
+ UseNewOffloadingDriver && C.isOffloadingHostKind(Action::OFK_HIP) &&
+ offloadDeviceOnly() && Args.hasArg(options::OPT_hip_link) &&
+ Args.hasFlag(options::OPT_fgpu_rdc, options::OPT_fno_gpu_rdc, false) &&
+ getFinalPhase(Args) == phases::Link &&
+ !Args.hasArg(options::OPT_emit_llvm) &&
+ Args.hasFlag(options::OPT_gpu_bundle_output,
+ options::OPT_no_gpu_bundle_output, true);
// Builder to be used to build offloading actions.
std::unique_ptr<OffloadingActionBuilder> OffloadBuilder =
@@ -4567,6 +4575,12 @@ void Driver::BuildActions(Compilation &C, DerivedArgList &Args,
// Build the pipeline for this file.
Action *Current = C.MakeAction<InputAction>(*InputArg, InputType);
+ // Device-only HIP links consume packaged offload bitcode directly.
+ if (HIPRDCDeviceOnlyFatBin && InputType == types::TY_LLVM_BC) {
+ LinkerInputs.push_back(Current);
+ continue;
+ }
+
std::string CUID;
if (CUIDOpts.isEnabled() && types::isSrcFile(InputType)) {
CUID = CUIDOpts.getCUID(InputArg->getValue(), Args);
@@ -4693,7 +4707,9 @@ void Driver::BuildActions(Compilation &C, DerivedArgList &Args,
LA = C.MakeAction<StaticLibJobAction>(LinkerInputs, types::TY_Image);
} else if (UseNewOffloadingDriver ||
Args.hasArg(options::OPT_offload_link)) {
- LA = C.MakeAction<LinkerWrapperJobAction>(LinkerInputs, types::TY_Image);
+ LA = C.MakeAction<LinkerWrapperJobAction>(
+ LinkerInputs,
+ HIPRDCDeviceOnlyFatBin ? types::TY_HIP_FATBIN : types::TY_Image);
LA->propagateHostOffloadInfo(C.getActiveOffloadKinds(),
/*BA=*/{});
} else {
diff --git a/clang/lib/Driver/ToolChains/Clang.cpp b/clang/lib/Driver/ToolChains/Clang.cpp
index 0904d116b5cba..94f9a26aac39f 100644
--- a/clang/lib/Driver/ToolChains/Clang.cpp
+++ b/clang/lib/Driver/ToolChains/Clang.cpp
@@ -9839,6 +9839,11 @@ void LinkerWrapper::ConstructJob(Compilation &C, const JobAction &JA,
OPT_fno_slp_vectorize,
OPT_hipstdpar};
const llvm::DenseSet<unsigned> LinkerOptions{OPT_mllvm, OPT_Zlinker_input};
+ // Suppress verbose output for HIP non-RDC fat binaries because it confuses
+ // CMake implicit linker argument parsing.
+ bool SuppressHIPNoRDCVerbose =
+ JA.getType() == types::TY_HIP_FATBIN &&
+ !Args.hasFlag(options::OPT_fgpu_rdc, options::OPT_fno_gpu_rdc, false);
auto ToolChainHasRT = [&](const ToolChain &TC, StringRef Name) {
return TC.getVFS().exists(
TC.getCompilerRT(Args, Name, ToolChain::FT_Static));
@@ -9860,8 +9865,7 @@ void LinkerWrapper::ConstructJob(Compilation &C, const JobAction &JA,
};
auto ShouldForward = [&](const llvm::DenseSet<unsigned> &Set, Arg *A,
const ToolChain &TC) {
- // CMake hack to avoid printing verbose informatoin for HIP non-RDC mode.
- if (A->getOption().matches(OPT_v) && JA.getType() == types::TY_HIP_FATBIN)
+ if (A->getOption().matches(OPT_v) && SuppressHIPNoRDCVerbose)
return false;
return (Set.contains(A->getOption().getID()) ||
(A->getOption().getGroup().isValid() &&
@@ -9979,11 +9983,7 @@ void LinkerWrapper::ConstructJob(Compilation &C, const JobAction &JA,
CmdArgs.push_back(Args.MakeArgString("--host-triple=" +
getToolChain().getTripleString()));
- // CMake hack, suppress passing verbose arguments for the special-case HIP
- // non-RDC mode compilation. This confuses default CMake implicit linker
- // argument parsing when the language is set to HIP and the system linker is
- // also `ld.lld`.
- if (Args.hasArg(options::OPT_v) && JA.getType() != types::TY_HIP_FATBIN)
+ if (Args.hasArg(options::OPT_v) && !SuppressHIPNoRDCVerbose)
CmdArgs.push_back("--wrapper-verbose");
if (Arg *A = Args.getLastArg(options::OPT_cuda_path_EQ)) {
CmdArgs.push_back(
@@ -10071,7 +10071,7 @@ void LinkerWrapper::ConstructJob(Compilation &C, const JobAction &JA,
// We use action type to
diff erentiate two use cases of the linker wrapper.
// TY_Image for normal linker wrapper work.
- // TY_HIP_FATBIN for HIP fno-gpu-rdc emitting a fat binary without wrapping.
+ // TY_HIP_FATBIN for HIP device-only links emitting a fat binary directly.
assert(JA.getType() == types::TY_HIP_FATBIN ||
JA.getType() == types::TY_Image);
if (JA.getType() == types::TY_HIP_FATBIN) {
diff --git a/clang/test/Driver/hip-link-offload-binary.c b/clang/test/Driver/hip-link-offload-binary.c
new file mode 100644
index 0000000000000..b318074130abd
--- /dev/null
+++ b/clang/test/Driver/hip-link-offload-binary.c
@@ -0,0 +1,59 @@
+// REQUIRES: amdgpu-registered-target
+// REQUIRES: lld
+
+// RUN: %clang --target=amdgcn-amd-amdhsa -emit-llvm -c -nogpulib -DVAR=x %s -o %t.x.bc
+// RUN: %clang --target=amdgcn-amd-amdhsa -emit-llvm -c -nogpulib -DVAR=y %s -o %t.y.bc
+// RUN: llvm-offload-binary -o %t.x.bundle.bc \
+// RUN: --image=file=%t.x.bc,triple=amdgcn-amd-amdhsa,arch=gfx906,kind=hip \
+// RUN: --image=file=%t.x.bc,triple=amdgcn-amd-amdhsa,arch=gfx942,kind=hip
+// RUN: llvm-offload-binary -o %t.y.bundle.bc \
+// RUN: --image=file=%t.y.bc,triple=amdgcn-amd-amdhsa,arch=gfx906,kind=hip \
+// RUN: --image=file=%t.y.bc,triple=amdgcn-amd-amdhsa,arch=gfx942,kind=hip
+
+// RUN: %clang -### --target=x86_64-unknown-linux-gnu \
+// RUN: -fgpu-rdc --hip-link --cuda-device-only \
+// RUN: --offload-arch=gfx906 --offload-arch=gfx942 \
+// RUN: %t.x.bundle.bc %t.y.bundle.bc -o %t.hipfb 2>&1 \
+// RUN: | FileCheck %s --check-prefix=DRIVER
+// DRIVER: "{{.*}}clang-linker-wrapper"
+// DRIVER-SAME: "--should-extract=gfx906"
+// DRIVER-SAME: "--should-extract=gfx942"
+// DRIVER-SAME: "--emit-fatbin-only" "-o" "{{.*}}.hipfb"
+// DRIVER-SAME: "{{.*}}.x.bundle.bc" "{{.*}}.y.bundle.bc"
+
+// RUN: %clang -### -v --target=x86_64-unknown-linux-gnu \
+// RUN: -fgpu-rdc --hip-link --cuda-device-only \
+// RUN: --offload-arch=gfx906 %t.x.bundle.bc -o %t.hipfb 2>&1 \
+// RUN: | FileCheck %s --check-prefix=VERBOSE
+// VERBOSE: "{{.*}}clang-linker-wrapper"
+// VERBOSE-SAME: "--device-compiler=amdgcn-amd-amdhsa=-v"
+// VERBOSE-SAME: "--wrapper-verbose"
+// VERBOSE-SAME: "--emit-fatbin-only"
+
+// RUN: %clang --target=x86_64-unknown-linux-gnu \
+// RUN: -fgpu-rdc --hip-link --cuda-device-only \
+// RUN: --offload-arch=gfx906 --offload-arch=gfx942 \
+// RUN: %t.x.bundle.bc %t.y.bundle.bc -o %t.hipfb
+// RUN: clang-offload-bundler -type=o -list -input=%t.hipfb \
+// RUN: | FileCheck %s --check-prefix=ARCH
+// ARCH-DAG: hip-amdgcn-amd-amdhsa--gfx906
+// ARCH-DAG: hip-amdgcn-amd-amdhsa--gfx942
+
+// RUN: %clang -### -c --target=x86_64-unknown-linux-gnu \
+// RUN: -fgpu-rdc --hip-link --cuda-device-only \
+// RUN: --offload-arch=gfx906 %t.x.bundle.bc -o %t.o 2>&1 \
+// RUN: | FileCheck %s --check-prefix=COMPILE
+// COMPILE: "-cc1"
+// COMPILE-NOT: "{{.*}}clang-linker-wrapper"
+
+// RUN: %clang -### -emit-llvm --target=x86_64-unknown-linux-gnu \
+// RUN: -fgpu-rdc --hip-link --cuda-device-only \
+// RUN: --offload-arch=gfx906 %t.x.bundle.bc -o %t.linked.bc 2>&1 \
+// RUN: | FileCheck %s --check-prefix=NO-FATBIN --allow-empty
+// RUN: %clang -### --no-gpu-bundle-output \
+// RUN: --target=x86_64-unknown-linux-gnu -fgpu-rdc --hip-link \
+// RUN: --cuda-device-only --offload-arch=gfx906 %t.x.bundle.bc 2>&1 \
+// RUN: | FileCheck %s --check-prefix=NO-FATBIN --allow-empty
+// NO-FATBIN-NOT: "--emit-fatbin-only"
+
+__attribute__((visibility("protected"), used)) int VAR;
More information about the cfe-commits
mailing list