https://github.com/jhuber6 updated 
https://github.com/llvm/llvm-project/pull/188584

>From 766487bbf742748348d41272b1fde0c62d04450b Mon Sep 17 00:00:00 2001
From: Joseph Huber <[email protected]>
Date: Wed, 25 Mar 2026 12:06:06 -0500
Subject: [PATCH 1/2] [Clang] Enable multilib library support for Linux/Windows

Summary:
This PR standardizes the logic used in the baremtal build to the common
toolchain interface. We then use this to handle the support in Linux and
Windows.

The multilib functionality allows us to select variant libraries based
off of a configuration file. For example, if the `multilib.yaml` file
detects `-fsanitize=address` it will automatically use the libraries
inside of `asan/` instead. These are layered so they do not necessarily
need to be complete library builds. More documentation can be found at
https://clang.llvm.org/docs/Multilib.html.

The motivation for this is so platforms like ROCm can use a more
standard way to ship debug / asan variants of libraries like OpenMP or
similar for both GPU an CPU targets.
---
 clang/include/clang/Driver/ToolChain.h     |  23 ++++-
 clang/lib/Driver/ToolChain.cpp             | 103 +++++++++++++++++++++
 clang/lib/Driver/ToolChains/BareMetal.cpp  | 101 +-------------------
 clang/lib/Driver/ToolChains/BareMetal.h    |   9 --
 clang/lib/Driver/ToolChains/CommonArgs.cpp |  13 ++-
 clang/lib/Driver/ToolChains/Gnu.cpp        |  17 ++--
 clang/lib/Driver/ToolChains/Linux.cpp      |  15 +++
 clang/lib/Driver/ToolChains/MinGW.cpp      |  17 ++++
 clang/test/Driver/linux-multilib.yaml      |  78 ++++++++++++++++
 clang/test/Driver/mingw-multilib.yaml      |  78 ++++++++++++++++
 10 files changed, 333 insertions(+), 121 deletions(-)
 create mode 100644 clang/test/Driver/linux-multilib.yaml
 create mode 100644 clang/test/Driver/mingw-multilib.yaml

diff --git a/clang/include/clang/Driver/ToolChain.h 
b/clang/include/clang/Driver/ToolChain.h
index 5b3e2c3b486767..c1734f9a9bda4a 100644
--- a/clang/include/clang/Driver/ToolChain.h
+++ b/clang/include/clang/Driver/ToolChain.h
@@ -206,6 +206,21 @@ class ToolChain {
 protected:
   MultilibSet Multilibs;
   llvm::SmallVector<Multilib> SelectedMultilibs;
+  SmallVector<std::string> MultilibMacroDefines;
+
+  using OrderedMultilibs =
+      
llvm::iterator_range<llvm::SmallVector<Multilib>::const_reverse_iterator>;
+
+  /// Get selected multilibs in priority order with default fallback.
+  OrderedMultilibs getOrderedMultilibs() const;
+
+  /// Load multilib configuration from a YAML file at \p MultilibPath,
+  bool loadMultilibsFromYAML(const llvm::opt::ArgList &Args, const Driver &D,
+                             StringRef MultilibPath);
+
+  /// Discover and load a multilib.yaml configuration.
+  bool discoverMultilibsFromYAML(const llvm::opt::ArgList &Args,
+                                 const Driver &D, StringRef FallbackDir = {});
 
   ToolChain(const Driver &D, const llvm::Triple &T,
             const llvm::opt::ArgList &Args);
@@ -715,12 +730,12 @@ class ToolChain {
   /// Add warning options that need to be passed to cc1 for this target.
   virtual void addClangWarningOptions(llvm::opt::ArgStringList &CC1Args) const;
 
-  // Get the list of extra macro defines requested by the multilib
-  // configuration.
+  /// Get the list of extra macro defines requested by the multilib
+  /// configuration.
   virtual SmallVector<std::string>
   getMultilibMacroDefinesStr(llvm::opt::ArgList &Args) const {
-    return {};
-  };
+    return MultilibMacroDefines;
+  }
 
   // GetRuntimeLibType - Determine the runtime library type to use with the
   // given compilation arguments.
diff --git a/clang/lib/Driver/ToolChain.cpp b/clang/lib/Driver/ToolChain.cpp
index 945037369e4ee7..d03b654cb20fe3 100644
--- a/clang/lib/Driver/ToolChain.cpp
+++ b/clang/lib/Driver/ToolChain.cpp
@@ -39,6 +39,7 @@
 #include "llvm/Support/ErrorHandling.h"
 #include "llvm/Support/FileSystem.h"
 #include "llvm/Support/FileUtilities.h"
+#include "llvm/Support/MemoryBuffer.h"
 #include "llvm/Support/Path.h"
 #include "llvm/Support/Process.h"
 #include "llvm/Support/VersionTuple.h"
@@ -105,6 +106,108 @@ ToolChain::ToolChain(const Driver &D, const llvm::Triple 
&T,
     addIfExists(getFilePaths(), Path);
 }
 
+ToolChain::OrderedMultilibs ToolChain::getOrderedMultilibs() const {
+  if (!SelectedMultilibs.empty())
+    return llvm::reverse(SelectedMultilibs);
+
+  static const llvm::SmallVector<Multilib> Default = {Multilib()};
+  return llvm::reverse(Default);
+}
+
+bool ToolChain::loadMultilibsFromYAML(const llvm::opt::ArgList &Args,
+                                      const Driver &D, StringRef MultilibPath) 
{
+  llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>> MB =
+      D.getVFS().getBufferForFile(MultilibPath);
+  if (!MB)
+    return false;
+
+  Multilib::flags_list Flags = getMultilibFlags(Args);
+  llvm::ErrorOr<MultilibSet> ErrorOrMultilibSet =
+      MultilibSet::parseYaml(*MB.get());
+  if (ErrorOrMultilibSet.getError())
+    return false;
+
+  Multilibs = std::move(ErrorOrMultilibSet.get());
+
+  SmallVector<StringRef> CustomFlagMacroDefines;
+  bool Result =
+      Multilibs.select(D, Flags, SelectedMultilibs, &CustomFlagMacroDefines);
+
+  // Custom flag macro defines are set by processCustomFlags regardless of
+  // whether variant selection succeeds.
+  MultilibMacroDefines.clear();
+  for (StringRef Define : CustomFlagMacroDefines)
+    MultilibMacroDefines.push_back(Define.str());
+
+  if (!Result) {
+    D.Diag(clang::diag::warn_drv_missing_multilib) << llvm::join(Flags, " ");
+    SmallString<0> Data;
+    raw_svector_ostream OS(Data);
+    for (const Multilib &M : Multilibs)
+      if (!M.isError())
+        OS << "\n" << llvm::join(M.flags(), " ");
+    D.Diag(clang::diag::note_drv_available_multilibs) << OS.str();
+
+    for (const Multilib &M : SelectedMultilibs)
+      if (M.isError())
+        D.Diag(clang::diag::err_drv_multilib_custom_error)
+            << M.getErrorMessage();
+
+    SelectedMultilibs.clear();
+    return false;
+  }
+
+  // Prepend variant-specific library paths. The YAML's parent directory is
+  // the base for file paths; getRuntimePath() is the base for runtime paths.
+  StringRef YAMLBase = llvm::sys::path::parent_path(MultilibPath);
+  std::optional<std::string> RuntimeDir = getRuntimePath();
+  size_t FileInsertPos = 0;
+  size_t LibInsertPos = 0;
+  for (const Multilib &M : getOrderedMultilibs()) {
+    if (M.isDefault())
+      continue;
+    SmallString<128> FilePath(YAMLBase);
+    llvm::sys::path::append(FilePath, M.gccSuffix());
+    getFilePaths().insert(getFilePaths().begin() + FileInsertPos,
+                          std::string(FilePath));
+    ++FileInsertPos;
+    if (RuntimeDir) {
+      SmallString<128> LibPath(*RuntimeDir);
+      llvm::sys::path::append(LibPath, M.gccSuffix());
+      getLibraryPaths().insert(getLibraryPaths().begin() + LibInsertPos,
+                               std::string(LibPath));
+      ++LibInsertPos;
+    }
+  }
+
+  return true;
+}
+
+bool ToolChain::discoverMultilibsFromYAML(const llvm::opt::ArgList &Args,
+                                          const Driver &D,
+                                          StringRef FallbackDir) {
+  if (Arg *A = Args.getLastArg(options::OPT_multi_lib_config)) {
+    SmallString<128> MultilibPath(A->getValue());
+    if (!D.getVFS().exists(MultilibPath)) {
+      D.Diag(clang::diag::err_drv_no_such_file) << MultilibPath.str();
+      return false;
+    }
+    return loadMultilibsFromYAML(Args, D, MultilibPath);
+  }
+
+  SmallString<128> MultilibPath;
+  if (!FallbackDir.empty())
+    MultilibPath = FallbackDir;
+  else if (std::optional<std::string> StdlibDir = getStdlibPath())
+    MultilibPath = *StdlibDir;
+  else
+    return false;
+  llvm::sys::path::append(MultilibPath, "multilib.yaml");
+  if (!D.getVFS().exists(MultilibPath))
+    return false;
+  return loadMultilibsFromYAML(Args, D, MultilibPath);
+}
+
 void ToolChain::setTripleEnvironment(llvm::Triple::EnvironmentType Env) {
   Triple.setEnvironment(Env);
   if (EffectiveTriple != llvm::Triple())
diff --git a/clang/lib/Driver/ToolChains/BareMetal.cpp 
b/clang/lib/Driver/ToolChains/BareMetal.cpp
index 90b843aaed18a3..58f4fe8ceeea86 100644
--- a/clang/lib/Driver/ToolChains/BareMetal.cpp
+++ b/clang/lib/Driver/ToolChains/BareMetal.cpp
@@ -24,8 +24,6 @@
 #include "llvm/Support/Path.h"
 #include "llvm/Support/VirtualFileSystem.h"
 
-#include <sstream>
-
 using namespace llvm::opt;
 using namespace clang;
 using namespace clang::driver;
@@ -264,89 +262,15 @@ BareMetal::BareMetal(const Driver &D, const llvm::Triple 
&Triple,
   }
 }
 
-static void
-findMultilibsFromYAML(const ToolChain &TC, const Driver &D,
-                      StringRef MultilibPath, const ArgList &Args,
-                      DetectedMultilibs &Result,
-                      SmallVector<StringRef> &CustomFlagsMacroDefines) {
-  llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>> MB =
-      D.getVFS().getBufferForFile(MultilibPath);
-  if (!MB)
-    return;
-  Multilib::flags_list Flags = TC.getMultilibFlags(Args);
-  llvm::ErrorOr<MultilibSet> ErrorOrMultilibSet =
-      MultilibSet::parseYaml(*MB.get());
-  if (ErrorOrMultilibSet.getError())
-    return;
-  Result.Multilibs = ErrorOrMultilibSet.get();
-  if (Result.Multilibs.select(D, Flags, Result.SelectedMultilibs,
-                              &CustomFlagsMacroDefines))
-    return;
-  D.Diag(clang::diag::warn_drv_missing_multilib) << llvm::join(Flags, " ");
-  std::stringstream ss;
-
-  // If multilib selection didn't complete successfully, report a list
-  // of all the configurations the user could have provided.
-  for (const Multilib &Multilib : Result.Multilibs)
-    if (!Multilib.isError())
-      ss << "\n" << llvm::join(Multilib.flags(), " ");
-  D.Diag(clang::diag::note_drv_available_multilibs) << ss.str();
-
-  // Now report any custom error messages requested by the YAML. We do
-  // this after displaying the list of available multilibs, because
-  // that list is probably large, and (in interactive use) risks
-  // scrolling the useful error message off the top of the user's
-  // terminal.
-  for (const Multilib &Multilib : Result.SelectedMultilibs)
-    if (Multilib.isError())
-      D.Diag(clang::diag::err_drv_multilib_custom_error)
-          << Multilib.getErrorMessage();
-
-  // If there was an error, clear the SelectedMultilibs vector, in
-  // case it contains partial data.
-  Result.SelectedMultilibs.clear();
-}
-
-static constexpr llvm::StringLiteral MultilibFilename = "multilib.yaml";
-
-static std::optional<llvm::SmallString<128>>
-getMultilibConfigPath(const Driver &D, const llvm::Triple &Triple,
-                      const ArgList &Args) {
-  llvm::SmallString<128> MultilibPath;
-  if (Arg *ConfigFileArg = Args.getLastArg(options::OPT_multi_lib_config)) {
-    MultilibPath = ConfigFileArg->getValue();
-    if (!D.getVFS().exists(MultilibPath)) {
-      D.Diag(clang::diag::err_drv_no_such_file) << MultilibPath.str();
-      return {};
-    }
-  } else {
-    MultilibPath = computeClangRuntimesSysRoot(D, /*IncludeTriple=*/false);
-    llvm::sys::path::append(MultilibPath, MultilibFilename);
-  }
-  return MultilibPath;
-}
-
 void BareMetal::findMultilibs(const Driver &D, const llvm::Triple &Triple,
                               const ArgList &Args) {
-  DetectedMultilibs Result;
   // Look for a multilib.yaml before trying target-specific hardwired logic.
-  // If it exists, always do what it specifies.
-  std::optional<llvm::SmallString<128>> MultilibPath =
-      getMultilibConfigPath(D, Triple, Args);
-  if (!MultilibPath)
-    return;
-  if (D.getVFS().exists(*MultilibPath)) {
-    // If multilib.yaml is found, update sysroot so it doesn't use a target
-    // specific suffix
-    SysRoot = computeClangRuntimesSysRoot(D, /*IncludeTriple=*/false);
-    SmallVector<StringRef> CustomFlagMacroDefines;
-    findMultilibsFromYAML(*this, D, *MultilibPath, Args, Result,
-                          CustomFlagMacroDefines);
-    SelectedMultilibs = Result.SelectedMultilibs;
-    Multilibs = Result.Multilibs;
-    MultilibMacroDefines.append(CustomFlagMacroDefines.begin(),
-                                CustomFlagMacroDefines.end());
+  std::string FallbackDir =
+      computeClangRuntimesSysRoot(D, /*IncludeTriple=*/false);
+  if (discoverMultilibsFromYAML(Args, D, FallbackDir)) {
+    SysRoot = FallbackDir;
   } else if (isRISCVBareMetal(Triple) && !detectGCCToolchainAdjacent(D)) {
+    DetectedMultilibs Result;
     if (findRISCVMultilibs(D, Triple, Args, Result)) {
       SelectedMultilibs = Result.SelectedMultilibs;
       Multilibs = Result.Multilibs;
@@ -368,16 +292,6 @@ Tool *BareMetal::buildStaticLibTool() const {
   return new tools::baremetal::StaticLibTool(*this);
 }
 
-BareMetal::OrderedMultilibs BareMetal::getOrderedMultilibs() const {
-  // Get multilibs in reverse order because they're ordered most-specific last.
-  if (!SelectedMultilibs.empty())
-    return llvm::reverse(SelectedMultilibs);
-
-  // No multilibs selected so return a single default multilib.
-  static const llvm::SmallVector<Multilib> Default = {Multilib()};
-  return llvm::reverse(Default);
-}
-
 ToolChain::CXXStdlibType BareMetal::GetDefaultCXXStdlibType() const {
   if (getTriple().isRISCV() && IsGCCInstallationValid)
     return ToolChain::CST_Libstdcxx;
@@ -756,8 +670,3 @@ SanitizerMask BareMetal::getSupportedSanitizers() const {
   }
   return Res;
 }
-
-SmallVector<std::string>
-BareMetal::getMultilibMacroDefinesStr(llvm::opt::ArgList &Args) const {
-  return MultilibMacroDefines;
-}
diff --git a/clang/lib/Driver/ToolChains/BareMetal.h 
b/clang/lib/Driver/ToolChains/BareMetal.h
index d3d415b337a0b1..57bfb73e3399b4 100644
--- a/clang/lib/Driver/ToolChains/BareMetal.h
+++ b/clang/lib/Driver/ToolChains/BareMetal.h
@@ -79,19 +79,10 @@ class LLVM_LIBRARY_VISIBILITY BareMetal : public 
Generic_ELF {
   std::string getCompilerRTPath() const override;
   SanitizerMask getSupportedSanitizers() const override;
 
-  SmallVector<std::string>
-  getMultilibMacroDefinesStr(llvm::opt::ArgList &Args) const override;
-
 private:
-  using OrderedMultilibs =
-      
llvm::iterator_range<llvm::SmallVector<Multilib>::const_reverse_iterator>;
-  OrderedMultilibs getOrderedMultilibs() const;
-
   std::string SysRoot;
 
   bool IsGCCInstallationValid;
-
-  SmallVector<std::string> MultilibMacroDefines;
 };
 
 } // namespace toolchains
diff --git a/clang/lib/Driver/ToolChains/CommonArgs.cpp 
b/clang/lib/Driver/ToolChains/CommonArgs.cpp
index c04e2c2b5b8c52..23545bc84ae906 100644
--- a/clang/lib/Driver/ToolChains/CommonArgs.cpp
+++ b/clang/lib/Driver/ToolChains/CommonArgs.cpp
@@ -1416,9 +1416,16 @@ void tools::addArchSpecificRPath(const ToolChain &TC, 
const ArgList &Args,
     return;
 
   SmallVector<std::string> CandidateRPaths(TC.getArchSpecificLibPaths());
-  if (const auto CandidateRPath = TC.getStdlibPath())
-    CandidateRPaths.emplace_back(*CandidateRPath);
-
+  if (const auto StdlibPath = TC.getStdlibPath()) {
+    for (const Multilib &M : llvm::reverse(TC.getSelectedMultilibs())) {
+      if (M.isDefault())
+        continue;
+      SmallString<128> P(*StdlibPath);
+      llvm::sys::path::append(P, M.gccSuffix());
+      CandidateRPaths.emplace_back(std::string(P));
+    }
+    CandidateRPaths.emplace_back(*StdlibPath);
+  }
   for (const auto &CandidateRPath : CandidateRPaths) {
     if (TC.getVFS().exists(CandidateRPath)) {
       CmdArgs.push_back("-rpath");
diff --git a/clang/lib/Driver/ToolChains/Gnu.cpp 
b/clang/lib/Driver/ToolChains/Gnu.cpp
index d8d537ec14b89b..2e7f78127a406a 100644
--- a/clang/lib/Driver/ToolChains/Gnu.cpp
+++ b/clang/lib/Driver/ToolChains/Gnu.cpp
@@ -3135,22 +3135,21 @@ void Generic_GCC::AddMultilibPaths(const Driver &D,
                                    path_list &Paths) {
   // Add the multilib suffixed paths where they are available.
   if (GCCInstallation.isValid()) {
-    assert(!SelectedMultilibs.empty());
+    const Multilib &GCCMultilib = GCCInstallation.getMultilib();
     const llvm::Triple &GCCTriple = GCCInstallation.getTriple();
     const std::string &LibPath =
         std::string(GCCInstallation.getParentLibPath());
 
     // Sourcery CodeBench MIPS toolchain holds some libraries under
     // a biarch-like suffix of the GCC installation.
-    if (const auto &PathsCallback = Multilibs.filePathsCallback())
-      for (const auto &Path : PathsCallback(SelectedMultilibs.back()))
+    if (const auto &PathsCallback =
+            GCCInstallation.getMultilibs().filePathsCallback())
+      for (const auto &Path : PathsCallback(GCCMultilib))
         addPathIfExists(D, GCCInstallation.getInstallPath() + Path, Paths);
 
     // Add lib/gcc/$triple/$version, with an optional /multilib suffix.
-    addPathIfExists(D,
-                    GCCInstallation.getInstallPath() +
-                        SelectedMultilibs.back().gccSuffix(),
-                    Paths);
+    addPathIfExists(
+        D, GCCInstallation.getInstallPath() + GCCMultilib.gccSuffix(), Paths);
 
     // Add lib/gcc/$triple/$libdir
     // For GCC built with --enable-version-specific-runtime-libs.
@@ -3177,7 +3176,7 @@ void Generic_GCC::AddMultilibPaths(const Driver &D,
     // Clang diverges from GCC's behavior.
     addPathIfExists(D,
                     LibPath + "/../" + GCCTriple.str() + "/lib/../" + OSLibDir 
+
-                        SelectedMultilibs.back().osSuffix(),
+                        GCCMultilib.osSuffix(),
                     Paths);
 
     // If the GCC installation we found is inside of the sysroot, we want to
@@ -3221,7 +3220,7 @@ void Generic_GCC::AddMultilibIncludeArgs(const ArgList 
&DriverArgs,
                               Twine(LibPath) + "/../" + GCCTriple.str() +
                                   "/include");
 
-  const auto &Callback = Multilibs.includeDirsCallback();
+  const auto &Callback = GCCInstallation.getMultilibs().includeDirsCallback();
   if (Callback) {
     for (const auto &Path : Callback(GCCInstallation.getMultilib()))
       addExternCSystemIncludeIfExists(DriverArgs, CC1Args,
diff --git a/clang/lib/Driver/ToolChains/Linux.cpp 
b/clang/lib/Driver/ToolChains/Linux.cpp
index bde99a3dac860e..c3ec30926112fc 100644
--- a/clang/lib/Driver/ToolChains/Linux.cpp
+++ b/clang/lib/Driver/ToolChains/Linux.cpp
@@ -234,6 +234,9 @@ Linux::Linux(const Driver &D, const llvm::Triple &Triple, 
const ArgList &Args)
   GCCInstallation.init(Triple, Args);
   Multilibs = GCCInstallation.getMultilibs();
   SelectedMultilibs.assign({GCCInstallation.getMultilib()});
+
+  discoverMultilibsFromYAML(Args, D);
+
   llvm::Triple::ArchType Arch = Triple.getArch();
   std::string SysRoot = computeSysRoot();
   ToolChain::path_list &PPaths = getProgramPaths();
@@ -768,6 +771,18 @@ void Linux::AddClangSystemIncludeArgs(const ArgList 
&DriverArgs,
   if (std::optional<std::string> Path = getStdlibIncludePath())
     addSystemInclude(DriverArgs, CC1Args, *Path);
 
+  // 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 (D.getVFS().exists(Dir))
+        addSystemInclude(DriverArgs, CC1Args, Dir);
+    }
+  }
+
   // LOCAL_INCLUDE_DIR
   addSystemInclude(DriverArgs, CC1Args, concat(SysRoot, "/usr/local/include"));
   // TOOL_INCLUDE_DIR
diff --git a/clang/lib/Driver/ToolChains/MinGW.cpp 
b/clang/lib/Driver/ToolChains/MinGW.cpp
index 2c9a174069f70c..2ff4e7f91b0a2d 100644
--- a/clang/lib/Driver/ToolChains/MinGW.cpp
+++ b/clang/lib/Driver/ToolChains/MinGW.cpp
@@ -554,6 +554,8 @@ toolchains::MinGW::MinGW(const Driver &D, const 
llvm::Triple &Triple,
       getDriver().SysRoot.size())
     getFilePaths().push_back(Base + "lib");
 
+  discoverMultilibsFromYAML(Args, D);
+
   NativeLLVMSupport =
       Args.getLastArgValue(options::OPT_fuse_ld_EQ, D.getPreferredLinker())
           .equals_insensitive("lld");
@@ -704,6 +706,21 @@ void toolchains::MinGW::AddClangSystemIncludeArgs(const 
ArgList &DriverArgs,
   if (DriverArgs.hasArg(options::OPT_nostdlibinc))
     return;
 
+  if (std::optional<std::string> Path = getStdlibIncludePath())
+    addSystemInclude(DriverArgs, CC1Args, *Path);
+
+  // 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);
+    }
+  }
+
   addSystemInclude(DriverArgs, CC1Args,
                    Base + SubdirName + llvm::sys::path::get_separator() +
                        "include");
diff --git a/clang/test/Driver/linux-multilib.yaml 
b/clang/test/Driver/linux-multilib.yaml
new file mode 100644
index 00000000000000..fca4990bb72498
--- /dev/null
+++ b/clang/test/Driver/linux-multilib.yaml
@@ -0,0 +1,78 @@
+# 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-linux-gnu -fmultilib-flag=debug --sysroot= \
+# RUN:   | FileCheck --check-prefix=CHECK-DEBUG %s
+# CHECK-DEBUG:      "-cc1" "-triple" "x86_64-unknown-linux-gnu"
+# 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=x86_64-linux-gnu --sysroot= \
+# 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=x86_64-linux-gnu -fmultilib-flag=debug -fno-exceptions 
--sysroot= \
+# 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=x86_64-linux-gnu -fmultilib-flag=debug --sysroot= \
+# 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-linux-gnu --sysroot= \
+# 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-linux-gnu -fmultilib-flag=nonexistent --sysroot= \
+# 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-linux-gnu -fmultilib-flag=release --sysroot= \
+# 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
+...
diff --git a/clang/test/Driver/mingw-multilib.yaml 
b/clang/test/Driver/mingw-multilib.yaml
new file mode 100644
index 00000000000000..6ee415adfa54fb
--- /dev/null
+++ b/clang/test/Driver/mingw-multilib.yaml
@@ -0,0 +1,78 @@
+# 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-w64-windows-gnu -fmultilib-flag=debug \
+# RUN:   | FileCheck --check-prefix=CHECK-DEBUG %s
+# CHECK-DEBUG:      "-cc1" "-triple" "x86_64-w64-windows-gnu"
+# 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=x86_64-w64-windows-gnu \
+# 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=x86_64-w64-windows-gnu -fmultilib-flag=debug 
-fno-exceptions \
+# 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=x86_64-w64-windows-gnu -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-w64-windows-gnu \
+# 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-w64-windows-gnu -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-w64-windows-gnu -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
+...

>From beb339ce8a8cef8e23390e0becee52d83762bb7c Mon Sep 17 00:00:00 2001
From: Joseph Huber <[email protected]>
Date: Wed, 8 Apr 2026 08:12:58 -0500
Subject: [PATCH 2/2] Address comments

---
 clang/include/clang/Driver/ToolChain.h    | 13 ++++++------
 clang/lib/Driver/ToolChain.cpp            | 26 +++++++++++++----------
 clang/lib/Driver/ToolChains/BareMetal.cpp |  2 +-
 clang/lib/Driver/ToolChains/Linux.cpp     | 12 +++++------
 clang/lib/Driver/ToolChains/MinGW.cpp     |  8 +++----
 5 files changed, 33 insertions(+), 28 deletions(-)

diff --git a/clang/include/clang/Driver/ToolChain.h 
b/clang/include/clang/Driver/ToolChain.h
index c1734f9a9bda4a..8bda212312aeee 100644
--- a/clang/include/clang/Driver/ToolChain.h
+++ b/clang/include/clang/Driver/ToolChain.h
@@ -214,13 +214,14 @@ class ToolChain {
   /// Get selected multilibs in priority order with default fallback.
   OrderedMultilibs getOrderedMultilibs() const;
 
-  /// Load multilib configuration from a YAML file at \p MultilibPath,
+  /// Discover and load a multilib.yaml configuration.
   bool loadMultilibsFromYAML(const llvm::opt::ArgList &Args, const Driver &D,
-                             StringRef MultilibPath);
+                             StringRef Fallback = {});
 
-  /// Discover and load a multilib.yaml configuration.
-  bool discoverMultilibsFromYAML(const llvm::opt::ArgList &Args,
-                                 const Driver &D, StringRef FallbackDir = {});
+  /// Load multilib configuration from a YAML file at \p MultilibPath,
+  std::optional<std::string> findMultilibsYAML(const llvm::opt::ArgList &Args,
+                                               const Driver &D,
+                                               StringRef FallbackDir = {});
 
   ToolChain(const Driver &D, const llvm::Triple &T,
             const llvm::opt::ArgList &Args);
@@ -732,7 +733,7 @@ class ToolChain {
 
   /// Get the list of extra macro defines requested by the multilib
   /// configuration.
-  virtual SmallVector<std::string>
+  SmallVector<std::string>
   getMultilibMacroDefinesStr(llvm::opt::ArgList &Args) const {
     return MultilibMacroDefines;
   }
diff --git a/clang/lib/Driver/ToolChain.cpp b/clang/lib/Driver/ToolChain.cpp
index d03b654cb20fe3..c6ab3cb2103827 100644
--- a/clang/lib/Driver/ToolChain.cpp
+++ b/clang/lib/Driver/ToolChain.cpp
@@ -115,9 +115,13 @@ ToolChain::OrderedMultilibs 
ToolChain::getOrderedMultilibs() const {
 }
 
 bool ToolChain::loadMultilibsFromYAML(const llvm::opt::ArgList &Args,
-                                      const Driver &D, StringRef MultilibPath) 
{
+                                      const Driver &D, StringRef Fallback) {
+  std::optional<std::string> MultilibPath =
+      findMultilibsYAML(Args, D, Fallback);
+  if (!MultilibPath)
+    return false;
   llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>> MB =
-      D.getVFS().getBufferForFile(MultilibPath);
+      D.getVFS().getBufferForFile(*MultilibPath);
   if (!MB)
     return false;
 
@@ -159,7 +163,7 @@ bool ToolChain::loadMultilibsFromYAML(const 
llvm::opt::ArgList &Args,
 
   // Prepend variant-specific library paths. The YAML's parent directory is
   // the base for file paths; getRuntimePath() is the base for runtime paths.
-  StringRef YAMLBase = llvm::sys::path::parent_path(MultilibPath);
+  StringRef YAMLBase = llvm::sys::path::parent_path(*MultilibPath);
   std::optional<std::string> RuntimeDir = getRuntimePath();
   size_t FileInsertPos = 0;
   size_t LibInsertPos = 0;
@@ -183,16 +187,16 @@ bool ToolChain::loadMultilibsFromYAML(const 
llvm::opt::ArgList &Args,
   return true;
 }
 
-bool ToolChain::discoverMultilibsFromYAML(const llvm::opt::ArgList &Args,
-                                          const Driver &D,
-                                          StringRef FallbackDir) {
+std::optional<std::string>
+ToolChain::findMultilibsYAML(const llvm::opt::ArgList &Args, const Driver &D,
+                             StringRef FallbackDir) {
   if (Arg *A = Args.getLastArg(options::OPT_multi_lib_config)) {
     SmallString<128> MultilibPath(A->getValue());
     if (!D.getVFS().exists(MultilibPath)) {
       D.Diag(clang::diag::err_drv_no_such_file) << MultilibPath.str();
-      return false;
+      return std::nullopt;
     }
-    return loadMultilibsFromYAML(Args, D, MultilibPath);
+    return std::string(MultilibPath);
   }
 
   SmallString<128> MultilibPath;
@@ -201,11 +205,11 @@ bool ToolChain::discoverMultilibsFromYAML(const 
llvm::opt::ArgList &Args,
   else if (std::optional<std::string> StdlibDir = getStdlibPath())
     MultilibPath = *StdlibDir;
   else
-    return false;
+    return std::nullopt;
   llvm::sys::path::append(MultilibPath, "multilib.yaml");
   if (!D.getVFS().exists(MultilibPath))
-    return false;
-  return loadMultilibsFromYAML(Args, D, MultilibPath);
+    return std::nullopt;
+  return std::string(MultilibPath);
 }
 
 void ToolChain::setTripleEnvironment(llvm::Triple::EnvironmentType Env) {
diff --git a/clang/lib/Driver/ToolChains/BareMetal.cpp 
b/clang/lib/Driver/ToolChains/BareMetal.cpp
index 58f4fe8ceeea86..9370eb21454e3c 100644
--- a/clang/lib/Driver/ToolChains/BareMetal.cpp
+++ b/clang/lib/Driver/ToolChains/BareMetal.cpp
@@ -267,7 +267,7 @@ void BareMetal::findMultilibs(const Driver &D, const 
llvm::Triple &Triple,
   // Look for a multilib.yaml before trying target-specific hardwired logic.
   std::string FallbackDir =
       computeClangRuntimesSysRoot(D, /*IncludeTriple=*/false);
-  if (discoverMultilibsFromYAML(Args, D, FallbackDir)) {
+  if (loadMultilibsFromYAML(Args, D, FallbackDir)) {
     SysRoot = FallbackDir;
   } else if (isRISCVBareMetal(Triple) && !detectGCCToolchainAdjacent(D)) {
     DetectedMultilibs Result;
diff --git a/clang/lib/Driver/ToolChains/Linux.cpp 
b/clang/lib/Driver/ToolChains/Linux.cpp
index c3ec30926112fc..c331f69c0bc2e6 100644
--- a/clang/lib/Driver/ToolChains/Linux.cpp
+++ b/clang/lib/Driver/ToolChains/Linux.cpp
@@ -235,7 +235,7 @@ Linux::Linux(const Driver &D, const llvm::Triple &Triple, 
const ArgList &Args)
   Multilibs = GCCInstallation.getMultilibs();
   SelectedMultilibs.assign({GCCInstallation.getMultilib()});
 
-  discoverMultilibsFromYAML(Args, D);
+  loadMultilibsFromYAML(Args, D);
 
   llvm::Triple::ArchType Arch = Triple.getArch();
   std::string SysRoot = computeSysRoot();
@@ -766,11 +766,6 @@ void Linux::AddClangSystemIncludeArgs(const ArgList 
&DriverArgs,
   if (DriverArgs.hasArg(options::OPT_nostdlibinc))
     return;
 
-  // After the resource directory, we prioritize the standard clang include
-  // directory.
-  if (std::optional<std::string> Path = getStdlibIncludePath())
-    addSystemInclude(DriverArgs, CC1Args, *Path);
-
   // Add multilib variant include paths in priority order.
   for (const Multilib &M : getOrderedMultilibs()) {
     if (M.isDefault())
@@ -783,6 +778,11 @@ void Linux::AddClangSystemIncludeArgs(const ArgList 
&DriverArgs,
     }
   }
 
+  // After the resource directory, we prioritize the standard clang include
+  // directory.
+  if (std::optional<std::string> Path = getStdlibIncludePath())
+    addSystemInclude(DriverArgs, CC1Args, *Path);
+
   // LOCAL_INCLUDE_DIR
   addSystemInclude(DriverArgs, CC1Args, concat(SysRoot, "/usr/local/include"));
   // TOOL_INCLUDE_DIR
diff --git a/clang/lib/Driver/ToolChains/MinGW.cpp 
b/clang/lib/Driver/ToolChains/MinGW.cpp
index 2ff4e7f91b0a2d..37b0ec7a62172f 100644
--- a/clang/lib/Driver/ToolChains/MinGW.cpp
+++ b/clang/lib/Driver/ToolChains/MinGW.cpp
@@ -554,7 +554,7 @@ toolchains::MinGW::MinGW(const Driver &D, const 
llvm::Triple &Triple,
       getDriver().SysRoot.size())
     getFilePaths().push_back(Base + "lib");
 
-  discoverMultilibsFromYAML(Args, D);
+  loadMultilibsFromYAML(Args, D);
 
   NativeLLVMSupport =
       Args.getLastArgValue(options::OPT_fuse_ld_EQ, D.getPreferredLinker())
@@ -706,9 +706,6 @@ void toolchains::MinGW::AddClangSystemIncludeArgs(const 
ArgList &DriverArgs,
   if (DriverArgs.hasArg(options::OPT_nostdlibinc))
     return;
 
-  if (std::optional<std::string> Path = getStdlibIncludePath())
-    addSystemInclude(DriverArgs, CC1Args, *Path);
-
   // Add multilib variant include paths in priority order.
   for (const Multilib &M : getOrderedMultilibs()) {
     if (M.isDefault())
@@ -721,6 +718,9 @@ void toolchains::MinGW::AddClangSystemIncludeArgs(const 
ArgList &DriverArgs,
     }
   }
 
+  if (std::optional<std::string> Path = getStdlibIncludePath())
+    addSystemInclude(DriverArgs, CC1Args, *Path);
+
   addSystemInclude(DriverArgs, CC1Args,
                    Base + SubdirName + llvm::sys::path::get_separator() +
                        "include");

_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to