[Clang] Support multilibs on Windows (#200212)

Summary:
This was pushed to a follow-up, and I think the previous has lingered
long enough to expand this to Windows. the per-target runtime directory
this uses is default-off, but nothing stops us from just providing this.
The interest in this is for ROCm builds on Windows to be able to provide
asan / debug builds the same way on Linux.
This commit is contained in:
Joseph Huber 2026-06-09 08:35:07 -05:00 committed by GitHub
parent 53870f797b
commit ee39c604fe
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
10 changed files with 113 additions and 1 deletions

View file

@ -48,7 +48,8 @@ EXPERIMENTAL Multilib via configuration file
============================================
Some Clang toolchains support loading multilib configuration from a
``multilib.yaml`` configuration file.
``multilib.yaml`` configuration file. This is currently supported on Linux,
Windows, and Baremetal toolchains.
A ``multilib.yaml`` configuration file specifies which multilib variants are
available, their relative location, what compilation options were used to build

View file

@ -325,6 +325,9 @@ Non-comprehensive list of changes in this release
- Updated support for Unicode from 15.1 to 18.0.
- Linux and Windows toolchains now support Clang multilibs using
``-fmultilib-flag=``.
- The SafeStack builtins ``__builtin___get_unsafe_stack_ptr``,
``__builtin___get_unsafe_stack_bottom``, ``__builtin___get_unsafe_stack_top``,
and ``__builtin___get_unsafe_stack_start`` are now deprecated. Use the

View file

@ -205,6 +205,10 @@ void visualstudio::Linker::ConstructJob(Compilation &C, const JobAction &JA,
if (TC.getVFS().exists(LibPath))
CmdArgs.push_back(Args.MakeArgString("-libpath:" + LibPath));
}
for (const auto &LibPath : TC.getFilePaths()) {
if (LibPath.length() > 0)
CmdArgs.push_back(Args.MakeArgString("-libpath:" + LibPath));
}
auto CRTPath = TC.getCompilerRTPath();
if (TC.getVFS().exists(CRTPath))
CmdArgs.push_back(Args.MakeArgString("-libpath:" + CRTPath));
@ -523,6 +527,8 @@ MSVCToolChain::MSVCToolChain(const Driver &D, const llvm::Triple &Triple,
llvm::findVCToolChainViaSetupConfig(getVFS(), VCToolsVersion,
VCToolChainPath, VSLayout) ||
llvm::findVCToolChainViaRegistry(VCToolChainPath, VSLayout);
loadMultilibsFromYAML(Args, D);
}
Tool *MSVCToolChain::buildLinker() const {
@ -774,6 +780,18 @@ void MSVCToolChain::AddClangSystemIncludeArgs(const ArgList &DriverArgs,
if (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);
}
}
// Honor %INCLUDE% and %EXTERNAL_INCLUDE%. It should have essential search
// paths set by vcvarsall.bat. Skip if the user expressly set any of the
// Windows SDK or VC Tools options.

View file

@ -0,0 +1,87 @@
# 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=x86_64-pc-windows-msvc -fmultilib-flag=debug \
# RUN: -ccc-install-dir %S/Inputs/multilib_msvc_tree/bin \
# RUN: | FileCheck --check-prefix=CHECK-DEBUG %s
# CHECK-DEBUG: "-cc1" "-triple" "x86_64-pc-windows-msvc
# CHECK-DEBUG: "-internal-isystem" "{{[^"]*}}{{[/\\]}}x86_64-pc-windows-msvc/debug"
# CHECK-DEBUG: "-libpath:{{[^"]*}}/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=x86_64-pc-windows-msvc \
# RUN: -ccc-install-dir %S/Inputs/multilib_msvc_tree/bin \
# RUN: | FileCheck --check-prefix=CHECK-DEFAULT %s
# CHECK-DEFAULT-NOT: "-libpath:{{[^"]*}}/debug"
# CHECK-DEFAULT-NOT: "-libpath:{{[^"]*}}/release"
# CHECK-DEFAULT-NOT: "-libpath:{{[^"]*}}/noexcept"
# CHECK-DEFAULT-NOT: "-internal-isystem" "{{[^"]*}}{{[/\\]}}x86_64-pc-windows-msvc/debug"
# CHECK-DEFAULT-NOT: "-internal-isystem" "{{[^"]*}}{{[/\\]}}x86_64-pc-windows-msvc/release"
# CHECK-DEFAULT-NOT: "-internal-isystem" "{{[^"]*}}{{[/\\]}}x86_64-pc-windows-msvc/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=x86_64-pc-windows-msvc -fmultilib-flag=debug -fno-exceptions \
# RUN: -ccc-install-dir %S/Inputs/multilib_msvc_tree/bin \
# RUN: | FileCheck --check-prefix=CHECK-LAYERED %s
# CHECK-LAYERED: "-internal-isystem" "{{[^"]*}}{{[/\\]}}x86_64-pc-windows-msvc/noexcept"
# CHECK-LAYERED-SAME: "-internal-isystem" "{{[^"]*}}{{[/\\]}}x86_64-pc-windows-msvc/debug"
# CHECK-LAYERED: "-libpath:{{[^"]*}}/noexcept"
# CHECK-LAYERED-SAME: "-libpath:{{[^"]*}}/debug"
# Lists selected variant directories.
# RUN: %clang --multi-lib-config=%s -no-canonical-prefixes -print-multi-directory 2>&1 \
# RUN: --target=x86_64-pc-windows-msvc -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=x86_64-pc-windows-msvc \
# 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=x86_64-pc-windows-msvc -fmultilib-flag=nonexistent \
# 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=x86_64-pc-windows-msvc -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

@ -1,6 +1,9 @@
// RUN: %clang -multi-lib-config=%S/Inputs/multilib/empty.yaml -print-multi-flags-experimental --target=aarch64-linux -fc++-abi=itanium -fsanitize=address | FileCheck --check-prefix=CHECK-LINUX %s
// CHECK-LINUX: --target=aarch64-unknown-linux
// RUN: %clang -multi-lib-config=%S/Inputs/multilib/empty.yaml -print-multi-flags-experimental --target=x86_64-pc-windows-msvc -fc++-abi=itanium -fsanitize=address | FileCheck --check-prefix=CHECK-WINDOWS %s
// CHECK-WINDOWS: --target=x86_64-pc-windows-msvc
// RUN: %clang -multi-lib-config=%S/Inputs/multilib/empty.yaml -print-multi-flags-experimental --target=aarch64-fuchsia -fsanitize=hwaddress | FileCheck --check-prefix=CHECK-FUCHSIA %s
// CHECK-FUCHSIA: --target=aarch64-unknown-fuchsia