[Clang] Add multilib support for GPU targets (#192285)

Summary:
This PR uses the new, generic multilib support added in
https://github.com/llvm/llvm-project/pull/188584
to also function for GPU targets. This will allow toolchains to easy
provide variants of these GPU libraries (for debug or asan). In
practice, this will look something like this:

```console
  -DRUNTIMES_amdgcn-amd-amdhsa+debug_CMAKE_BUILD_TYPE=Debug \
  -DRUNTIMES_amdgcn-amd-amdhsa+debug_LIBOMPTARGET_ENABLE_DEBUG=ON \
  -DRUNTIMES_amdgcn-amd-amdhsa+debug_LLVM_ENABLE_RUNTIMES=openmp \
  -DLLVM_RUNTIME_MULTILIBS=debug \
  -DLLVM_RUNTIME_MULTILIB_debug_TARGETS="amdgcn-amd-amdhsa" \
```

This will then install it into the tree like this:
```
<install>/lib/amdgcn-amd-amdhsa/debug/libompdevice.a
```

The user can then activate this like the following (assuming they have a
multilib.yaml in the library directory):
```
clang input.c -fopenmp --offload-arch=gfx942 -fmultilib-flag=debug
```
This commit is contained in:
Joseph Huber
2026-04-16 14:15:28 -05:00
committed by GitHub
parent ca3bc44c30
commit 9931b7830f
5 changed files with 190 additions and 1 deletions

View File

@@ -700,6 +700,8 @@ AMDGPUToolChain::AMDGPUToolChain(const Driver &D, const llvm::Triple &Triple,
: Generic_ELF(D, Triple, Args),
OptionsDefault(
{{options::OPT_O, "3"}, {options::OPT_cl_std_EQ, "CL1.2"}}) {
loadMultilibsFromYAML(Args, D);
// Check code object version options. Emit warnings for legacy options
// and errors for the last invalid code object version options.
// It is done here to avoid repeated warning or error messages for
@@ -881,6 +883,18 @@ void AMDGPUToolChain::AddClangSystemIncludeArgs(const ArgList &DriverArgs,
DriverArgs.hasArg(options::OPT_nostdlibinc))
return;
// Add multilib variant include paths in priority order.
for (const Multilib &M : getOrderedMultilibs()) {
if (M.isDefault())
continue;
if (std::optional<std::string> StdlibIncDir = getStdlibIncludePath()) {
SmallString<128> Dir(*StdlibIncDir);
llvm::sys::path::append(Dir, M.includeSuffix());
if (getDriver().getVFS().exists(Dir))
addSystemInclude(DriverArgs, CC1Args, Dir);
}
}
if (std::optional<std::string> Path = getStdlibIncludePath())
addSystemInclude(DriverArgs, CC1Args, *Path);
}

View File

@@ -9431,6 +9431,7 @@ void LinkerWrapper::ConstructJob(Compilation &C, const JobAction &JA,
OPT_flto_EQ,
OPT_hipspv_pass_plugin_EQ,
OPT_use_spirv_backend,
OPT_fmultilib_flag,
OPT_fprofile_generate,
OPT_fprofile_generate_EQ,
OPT_fprofile_instr_generate,

View File

@@ -742,7 +742,9 @@ NVPTXToolChain::NVPTXToolChain(const Driver &D, const llvm::Triple &Triple,
/// system's default triple if not provided.
NVPTXToolChain::NVPTXToolChain(const Driver &D, const llvm::Triple &Triple,
const ArgList &Args)
: NVPTXToolChain(D, Triple, llvm::Triple(LLVM_HOST_TRIPLE), Args) {}
: NVPTXToolChain(D, Triple, llvm::Triple(LLVM_HOST_TRIPLE), Args) {
loadMultilibsFromYAML(Args, D);
}
llvm::opt::DerivedArgList *
NVPTXToolChain::TranslateArgs(const llvm::opt::DerivedArgList &Args,
@@ -792,6 +794,18 @@ void NVPTXToolChain::AddClangSystemIncludeArgs(const ArgList &DriverArgs,
DriverArgs.hasArg(options::OPT_nostdlibinc))
return;
// Add multilib variant include paths in priority order.
for (const Multilib &M : getOrderedMultilibs()) {
if (M.isDefault())
continue;
if (std::optional<std::string> StdlibIncDir = getStdlibIncludePath()) {
SmallString<128> Dir(*StdlibIncDir);
llvm::sys::path::append(Dir, M.includeSuffix());
if (getDriver().getVFS().exists(Dir))
addSystemInclude(DriverArgs, CC1Args, Dir);
}
}
if (std::optional<std::string> Path = getStdlibIncludePath())
addSystemInclude(DriverArgs, CC1Args, *Path);
}

View File

@@ -0,0 +1,80 @@
# UNSUPPORTED: system-windows
# Basic selection where -fmultilib-flag=debug selects the debug variant.
# RUN: %clang --multi-lib-config=%s -no-canonical-prefixes -x c %s -### -o /dev/null 2>&1 \
# RUN: --target=amdgcn-amd-amdhsa -mcpu=gfx1030 -fmultilib-flag=debug -nogpulib \
# RUN: | FileCheck --check-prefix=CHECK-DEBUG %s
# CHECK-DEBUG: "-cc1" "-triple" "amdgcn-amd-amdhsa"
# CHECK-DEBUG: "-L{{[^"]*}}/debug"
# Default behavior where no variant path is prepended.
# RUN: %clang --multi-lib-config=%s -no-canonical-prefixes -x c %s -### -o /dev/null 2>&1 \
# RUN: --target=amdgcn-amd-amdhsa -mcpu=gfx1030 -nogpulib \
# RUN: | FileCheck --check-prefix=CHECK-DEFAULT %s
# CHECK-DEFAULT-NOT: "-L{{[^"]*}}/debug"
# CHECK-DEFAULT-NOT: "-L{{[^"]*}}/release"
# CHECK-DEFAULT-NOT: "-L{{[^"]*}}/noexcept"
# Multiple matches stacking on top of each-other.
# RUN: %clang --multi-lib-config=%s -no-canonical-prefixes -x c %s -### -o /dev/null 2>&1 \
# RUN: --target=amdgcn-amd-amdhsa -mcpu=gfx1030 -fmultilib-flag=debug -fno-exceptions -nogpulib \
# RUN: | FileCheck --check-prefix=CHECK-LAYERED %s
# CHECK-LAYERED: "-L{{[^"]*}}/noexcept"
# CHECK-LAYERED-SAME: "-L{{[^"]*}}/debug"
# Lists selected variant directories.
# RUN: %clang --multi-lib-config=%s -no-canonical-prefixes -print-multi-directory 2>&1 \
# RUN: --target=amdgcn-amd-amdhsa -mcpu=gfx1030 -fmultilib-flag=debug \
# RUN: | FileCheck --check-prefix=CHECK-PRINT-DIR %s
# CHECK-PRINT-DIR: debug
# Lists all non-default variants with flags.
# RUN: %clang --multi-lib-config=%s -no-canonical-prefixes -print-multi-lib 2>&1 \
# RUN: --target=amdgcn-amd-amdhsa -mcpu=gfx1030 \
# RUN: | FileCheck --check-prefix=CHECK-PRINT-LIB %s
# CHECK-PRINT-LIB: debug;@fmultilib-flag=debug
# CHECK-PRINT-LIB: release;@fmultilib-flag=release
# CHECK-PRINT-LIB: noexcept;@fno-exceptions
# Error emitted when custom flag value is invalid.
# RUN: not %clang --multi-lib-config=%s -no-canonical-prefixes -x c %s -### -o /dev/null 2>&1 \
# RUN: --target=amdgcn-amd-amdhsa -mcpu=gfx1030 -fmultilib-flag=nonexistent -nogpulib \
# RUN: | FileCheck --check-prefix=CHECK-NOMATCH %s
# CHECK-NOMATCH: error: unsupported option '-fmultilib-flag=nonexistent'
# Check exclusivity so that only one of debug/release selected.
# RUN: %clang --multi-lib-config=%s -no-canonical-prefixes -print-multi-directory 2>&1 \
# RUN: --target=amdgcn-amd-amdhsa -mcpu=gfx1030 -fmultilib-flag=release \
# RUN: | FileCheck --check-prefix=CHECK-EXCLUSIVE %s
# CHECK-EXCLUSIVE: release
# CHECK-EXCLUSIVE-NOT: debug
---
MultilibVersion: 1.0
Groups:
- Name: build-type
Type: Exclusive
Variants:
- Dir: .
Flags: []
- Dir: debug
Flags: [-fmultilib-flag=debug]
Group: build-type
- Dir: release
Flags: [-fmultilib-flag=release]
Group: build-type
- Dir: noexcept
Flags: [-fno-exceptions]
Mappings: []
Flags:
- Name: build-type
Values:
- Name: none
- Name: debug
- Name: release
Default: none
...

View File

@@ -0,0 +1,80 @@
# UNSUPPORTED: system-windows
# Basic selection where -fmultilib-flag=debug selects the debug variant.
# RUN: %clang --multi-lib-config=%s -no-canonical-prefixes -x c %s -### -o /dev/null 2>&1 \
# RUN: --target=nvptx64-nvidia-cuda -march=sm_52 -fmultilib-flag=debug -nogpulib \
# RUN: | FileCheck --check-prefix=CHECK-DEBUG %s
# CHECK-DEBUG: "-cc1" "-triple" "nvptx64-nvidia-cuda"
# CHECK-DEBUG: "-L{{[^"]*}}/debug"
# Default behavior where no variant path is prepended.
# RUN: %clang --multi-lib-config=%s -no-canonical-prefixes -x c %s -### -o /dev/null 2>&1 \
# RUN: --target=nvptx64-nvidia-cuda -march=sm_52 -nogpulib \
# RUN: | FileCheck --check-prefix=CHECK-DEFAULT %s
# CHECK-DEFAULT-NOT: "-L{{[^"]*}}/debug"
# CHECK-DEFAULT-NOT: "-L{{[^"]*}}/release"
# CHECK-DEFAULT-NOT: "-L{{[^"]*}}/noexcept"
# Multiple matches stacking on top of each-other.
# RUN: %clang --multi-lib-config=%s -no-canonical-prefixes -x c %s -### -o /dev/null 2>&1 \
# RUN: --target=nvptx64-nvidia-cuda -march=sm_52 -fmultilib-flag=debug -fno-exceptions -nogpulib \
# RUN: | FileCheck --check-prefix=CHECK-LAYERED %s
# CHECK-LAYERED: "-L{{[^"]*}}/noexcept"
# CHECK-LAYERED-SAME: "-L{{[^"]*}}/debug"
# Lists selected variant directories.
# RUN: %clang --multi-lib-config=%s -no-canonical-prefixes -print-multi-directory 2>&1 \
# RUN: --target=nvptx64-nvidia-cuda -march=sm_52 -fmultilib-flag=debug \
# RUN: | FileCheck --check-prefix=CHECK-PRINT-DIR %s
# CHECK-PRINT-DIR: debug
# Lists all non-default variants with flags.
# RUN: %clang --multi-lib-config=%s -no-canonical-prefixes -print-multi-lib 2>&1 \
# RUN: --target=nvptx64-nvidia-cuda -march=sm_52 \
# RUN: | FileCheck --check-prefix=CHECK-PRINT-LIB %s
# CHECK-PRINT-LIB: debug;@fmultilib-flag=debug
# CHECK-PRINT-LIB: release;@fmultilib-flag=release
# CHECK-PRINT-LIB: noexcept;@fno-exceptions
# Error emitted when custom flag value is invalid.
# RUN: not %clang --multi-lib-config=%s -no-canonical-prefixes -x c %s -### -o /dev/null 2>&1 \
# RUN: --target=nvptx64-nvidia-cuda -march=sm_52 -fmultilib-flag=nonexistent -nogpulib \
# RUN: | FileCheck --check-prefix=CHECK-NOMATCH %s
# CHECK-NOMATCH: error: unsupported option '-fmultilib-flag=nonexistent'
# Check exclusivity so that only one of debug/release selected.
# RUN: %clang --multi-lib-config=%s -no-canonical-prefixes -print-multi-directory 2>&1 \
# RUN: --target=nvptx64-nvidia-cuda -march=sm_52 -fmultilib-flag=release \
# RUN: | FileCheck --check-prefix=CHECK-EXCLUSIVE %s
# CHECK-EXCLUSIVE: release
# CHECK-EXCLUSIVE-NOT: debug
---
MultilibVersion: 1.0
Groups:
- Name: build-type
Type: Exclusive
Variants:
- Dir: .
Flags: []
- Dir: debug
Flags: [-fmultilib-flag=debug]
Group: build-type
- Dir: release
Flags: [-fmultilib-flag=release]
Group: build-type
- Dir: noexcept
Flags: [-fno-exceptions]
Mappings: []
Flags:
- Name: build-type
Values:
- Name: none
- Name: debug
- Name: release
Default: none
...