[llvm-branch-commits] [clang] [Multilib] Custom flags processing for library selection (PR #110659)

Victor Campos via llvm-branch-commits llvm-branch-commits at lists.llvm.org
Wed Nov 20 06:47:50 PST 2024


https://github.com/vhscampos updated https://github.com/llvm/llvm-project/pull/110659

>From b2de2588d108fa19a957a1525a63d522dee93769 Mon Sep 17 00:00:00 2001
From: Victor Campos <victor.campos at arm.com>
Date: Thu, 26 Sep 2024 14:44:33 +0100
Subject: [PATCH] [Multilib] Custom flags processing for library selection

Select library variants in the multilib system using the flags passed
following the '-fmultilib-flag=' format.

Multilib flags that were not passed in the command-line have their
default value fed into the library selection mechanism.

A warning is shown if the flag's value name is invalid. If the wrong
name is close enough to any valid one, according to edit distance, the
closest valid value name is suggested.

Details about this change can be found in this thread:
https://discourse.llvm.org/t/rfc-multilib-custom-flags/81058
---
 clang/include/clang/Driver/Driver.h           |   2 +-
 clang/include/clang/Driver/Multilib.h         |  11 +-
 clang/include/clang/Driver/ToolChain.h        |   7 +
 clang/lib/Driver/Driver.cpp                   | 142 ++++++++++--------
 clang/lib/Driver/Multilib.cpp                 | 136 ++++++++++++++++-
 clang/lib/Driver/ToolChains/BareMetal.cpp     |  22 ++-
 clang/lib/Driver/ToolChains/BareMetal.h       |   5 +
 .../baremetal-multilib-custom-flags.yaml      |  79 ++++++++++
 8 files changed, 329 insertions(+), 75 deletions(-)
 create mode 100644 clang/test/Driver/baremetal-multilib-custom-flags.yaml

diff --git a/clang/include/clang/Driver/Driver.h b/clang/include/clang/Driver/Driver.h
index 9177d56718ee77..40e2b9546cead5 100644
--- a/clang/include/clang/Driver/Driver.h
+++ b/clang/include/clang/Driver/Driver.h
@@ -463,7 +463,7 @@ class Driver {
   /// ArgList.
   llvm::opt::InputArgList ParseArgStrings(ArrayRef<const char *> Args,
                                           bool UseDriverMode,
-                                          bool &ContainsError);
+                                          bool &ContainsError) const;
 
   /// BuildInputs - Construct the list of inputs and their types from
   /// the given arguments.
diff --git a/clang/include/clang/Driver/Multilib.h b/clang/include/clang/Driver/Multilib.h
index 69d0b9b31714ff..d9398208fbea49 100644
--- a/clang/include/clang/Driver/Multilib.h
+++ b/clang/include/clang/Driver/Multilib.h
@@ -163,9 +163,18 @@ class MultilibSet {
   const_iterator begin() const { return Multilibs.begin(); }
   const_iterator end() const { return Multilibs.end(); }
 
+  /// Process custom flags from \p Flags and returns an expanded flags list and
+  /// a list of extra compilation arguments.
+  /// Returns a pair where:
+  ///  - first: the new flags list including custom flags after processing.
+  ///  - second: the extra compilation arguments to be fed to the driver.
+  std::pair<Multilib::flags_list, SmallVector<StringRef>>
+  processCustomFlags(const Driver &D, const Multilib::flags_list &Flags) const;
+
   /// Select compatible variants, \returns false if none are compatible
   bool select(const Driver &D, const Multilib::flags_list &Flags,
-              llvm::SmallVectorImpl<Multilib> &) const;
+              llvm::SmallVectorImpl<Multilib> &,
+              llvm::SmallVector<StringRef> * = nullptr) const;
 
   unsigned size() const { return Multilibs.size(); }
 
diff --git a/clang/include/clang/Driver/ToolChain.h b/clang/include/clang/Driver/ToolChain.h
index 5347e29be91439..25f51b7de3e9f5 100644
--- a/clang/include/clang/Driver/ToolChain.h
+++ b/clang/include/clang/Driver/ToolChain.h
@@ -686,6 +686,13 @@ 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 driver arguments strings requested by the multilib
+  // configuration.
+  virtual SmallVector<std::string>
+  getMultilibDriverArgsStr(llvm::opt::ArgList &Args) const {
+    return {};
+  };
+
   // GetRuntimeLibType - Determine the runtime library type to use with the
   // given compilation arguments.
   virtual RuntimeLibType
diff --git a/clang/lib/Driver/Driver.cpp b/clang/lib/Driver/Driver.cpp
index a0f4329e36136b..360f6519360d44 100644
--- a/clang/lib/Driver/Driver.cpp
+++ b/clang/lib/Driver/Driver.cpp
@@ -262,7 +262,7 @@ void Driver::setDriverMode(StringRef Value) {
 }
 
 InputArgList Driver::ParseArgStrings(ArrayRef<const char *> ArgStrings,
-                                     bool UseDriverMode, bool &ContainsError) {
+                                     bool UseDriverMode, bool &ContainsError) const {
   llvm::PrettyStackTraceString CrashInfo("Command line argument parsing");
   ContainsError = false;
 
@@ -1252,8 +1252,9 @@ Compilation *Driver::BuildCompilation(ArrayRef<const char *> ArgList) {
   bool HasConfigFile = !ContainsError && (CfgOptions.get() != nullptr);
 
   // All arguments, from both config file and command line.
-  InputArgList Args = std::move(HasConfigFile ? std::move(*CfgOptions)
-                                              : std::move(*CLOptions));
+  auto UArgs = std::make_unique<InputArgList>(std::move(HasConfigFile ? std::move(*CfgOptions)
+                                              : std::move(*CLOptions)));
+  InputArgList &Args = *UArgs;
 
   if (HasConfigFile)
     for (auto *Opt : *CLOptions) {
@@ -1287,52 +1288,6 @@ Compilation *Driver::BuildCompilation(ArrayRef<const char *> ArgList) {
     }
   }
 
-  // Check for working directory option before accessing any files
-  if (Arg *WD = Args.getLastArg(options::OPT_working_directory))
-    if (VFS->setCurrentWorkingDirectory(WD->getValue()))
-      Diag(diag::err_drv_unable_to_set_working_directory) << WD->getValue();
-
-  // Check for missing include directories.
-  if (!Diags.isIgnored(diag::warn_missing_include_dirs, SourceLocation())) {
-    for (auto IncludeDir : Args.getAllArgValues(options::OPT_I_Group)) {
-      if (!VFS->exists(IncludeDir))
-        Diag(diag::warn_missing_include_dirs) << IncludeDir;
-    }
-  }
-
-  // FIXME: This stuff needs to go into the Compilation, not the driver.
-  bool CCCPrintPhases;
-
-  // -canonical-prefixes, -no-canonical-prefixes are used very early in main.
-  Args.ClaimAllArgs(options::OPT_canonical_prefixes);
-  Args.ClaimAllArgs(options::OPT_no_canonical_prefixes);
-
-  // f(no-)integated-cc1 is also used very early in main.
-  Args.ClaimAllArgs(options::OPT_fintegrated_cc1);
-  Args.ClaimAllArgs(options::OPT_fno_integrated_cc1);
-
-  // Ignore -pipe.
-  Args.ClaimAllArgs(options::OPT_pipe);
-
-  // Extract -ccc args.
-  //
-  // FIXME: We need to figure out where this behavior should live. Most of it
-  // should be outside in the client; the parts that aren't should have proper
-  // options, either by introducing new ones or by overloading gcc ones like -V
-  // or -b.
-  CCCPrintPhases = Args.hasArg(options::OPT_ccc_print_phases);
-  CCCPrintBindings = Args.hasArg(options::OPT_ccc_print_bindings);
-  if (const Arg *A = Args.getLastArg(options::OPT_ccc_gcc_name))
-    CCCGenericGCCName = A->getValue();
-
-  // Process -fproc-stat-report options.
-  if (const Arg *A = Args.getLastArg(options::OPT_fproc_stat_report_EQ)) {
-    CCPrintProcessStats = true;
-    CCPrintStatReportFilename = A->getValue();
-  }
-  if (Args.hasArg(options::OPT_fproc_stat_report))
-    CCPrintProcessStats = true;
-
   // FIXME: TargetTriple is used by the target-prefixed calls to as/ld
   // and getToolChain is const.
   if (IsCLMode()) {
@@ -1385,6 +1340,79 @@ Compilation *Driver::BuildCompilation(ArrayRef<const char *> ArgList) {
     TargetTriple = A->getValue();
   if (const Arg *A = Args.getLastArg(options::OPT_ccc_install_dir))
     Dir = Dir = A->getValue();
+  if (const Arg *A = Args.getLastArg(options::OPT__sysroot_EQ))
+    SysRoot = A->getValue();
+  if (const Arg *A = Args.getLastArg(options::OPT_resource_dir))
+    ResourceDir = A->getValue();
+  if (const Arg *A = Args.getLastArg(options::OPT__dyld_prefix_EQ))
+    DyldPrefix = A->getValue();
+
+  setLTOMode(Args);
+
+  // Owned by the host.
+  const ToolChain &TC =
+      getToolChain(Args, computeTargetTriple(*this, TargetTriple, Args));
+
+  SmallVector<std::string> MultilibDriverArgsStr =
+      TC.getMultilibDriverArgsStr(Args);
+  SmallVector<const char *> MLArgsChar(
+      llvm::map_range(MultilibDriverArgsStr, [&Args](const auto &S) {
+        return Args.MakeArgString(S);
+      }));
+  bool MLContainsError;
+  auto MultilibDriverArgList = std::make_unique<InputArgList>(
+      ParseArgStrings(MLArgsChar, /*UseDriverMode=*/false, MLContainsError));
+  if (!MLContainsError)
+    for (auto *Opt : *MultilibDriverArgList) {
+      appendOneArg(Args, Opt, nullptr);
+    }
+
+  // Check for working directory option before accessing any files
+  if (Arg *WD = Args.getLastArg(options::OPT_working_directory))
+    if (VFS->setCurrentWorkingDirectory(WD->getValue()))
+      Diag(diag::err_drv_unable_to_set_working_directory) << WD->getValue();
+
+  // Check for missing include directories.
+  if (!Diags.isIgnored(diag::warn_missing_include_dirs, SourceLocation())) {
+    for (auto IncludeDir : Args.getAllArgValues(options::OPT_I_Group)) {
+      if (!VFS->exists(IncludeDir))
+        Diag(diag::warn_missing_include_dirs) << IncludeDir;
+    }
+  }
+
+  // FIXME: This stuff needs to go into the Compilation, not the driver.
+  bool CCCPrintPhases;
+
+  // -canonical-prefixes, -no-canonical-prefixes are used very early in main.
+  Args.ClaimAllArgs(options::OPT_canonical_prefixes);
+  Args.ClaimAllArgs(options::OPT_no_canonical_prefixes);
+
+  // f(no-)integated-cc1 is also used very early in main.
+  Args.ClaimAllArgs(options::OPT_fintegrated_cc1);
+  Args.ClaimAllArgs(options::OPT_fno_integrated_cc1);
+
+  // Ignore -pipe.
+  Args.ClaimAllArgs(options::OPT_pipe);
+
+  // Extract -ccc args.
+  //
+  // FIXME: We need to figure out where this behavior should live. Most of it
+  // should be outside in the client; the parts that aren't should have proper
+  // options, either by introducing new ones or by overloading gcc ones like -V
+  // or -b.
+  CCCPrintPhases = Args.hasArg(options::OPT_ccc_print_phases);
+  CCCPrintBindings = Args.hasArg(options::OPT_ccc_print_bindings);
+  if (const Arg *A = Args.getLastArg(options::OPT_ccc_gcc_name))
+    CCCGenericGCCName = A->getValue();
+
+  // Process -fproc-stat-report options.
+  if (const Arg *A = Args.getLastArg(options::OPT_fproc_stat_report_EQ)) {
+    CCPrintProcessStats = true;
+    CCPrintStatReportFilename = A->getValue();
+  }
+  if (Args.hasArg(options::OPT_fproc_stat_report))
+    CCPrintProcessStats = true;
+
   for (const Arg *A : Args.filtered(options::OPT_B)) {
     A->claim();
     PrefixDirs.push_back(A->getValue(0));
@@ -1399,13 +1427,6 @@ Compilation *Driver::BuildCompilation(ArrayRef<const char *> ArgList) {
       CompilerPath = Split.second;
     }
   }
-  if (const Arg *A = Args.getLastArg(options::OPT__sysroot_EQ))
-    SysRoot = A->getValue();
-  if (const Arg *A = Args.getLastArg(options::OPT__dyld_prefix_EQ))
-    DyldPrefix = A->getValue();
-
-  if (const Arg *A = Args.getLastArg(options::OPT_resource_dir))
-    ResourceDir = A->getValue();
 
   if (const Arg *A = Args.getLastArg(options::OPT_save_temps_EQ)) {
     SaveTemps = llvm::StringSwitch<SaveTempsMode>(A->getValue())
@@ -1425,8 +1446,6 @@ Compilation *Driver::BuildCompilation(ArrayRef<const char *> ArgList) {
       Offload = OffloadHostDevice;
   }
 
-  setLTOMode(Args);
-
   // Process -fembed-bitcode= flags.
   if (Arg *A = Args.getLastArg(options::OPT_fembed_bitcode_EQ)) {
     StringRef Name = A->getValue();
@@ -1480,16 +1499,9 @@ Compilation *Driver::BuildCompilation(ArrayRef<const char *> ArgList) {
     }
   }
 
-  std::unique_ptr<llvm::opt::InputArgList> UArgs =
-      std::make_unique<InputArgList>(std::move(Args));
-
   // Perform the default argument translations.
   DerivedArgList *TranslatedArgs = TranslateInputArgs(*UArgs);
 
-  // Owned by the host.
-  const ToolChain &TC = getToolChain(
-      *UArgs, computeTargetTriple(*this, TargetTriple, *UArgs));
-
   // Check if the environment version is valid except wasm case.
   llvm::Triple Triple = TC.getTriple();
   if (!Triple.isWasm()) {
diff --git a/clang/lib/Driver/Multilib.cpp b/clang/lib/Driver/Multilib.cpp
index 0d7d590a0eed8b..ae61f01c7c0252 100644
--- a/clang/lib/Driver/Multilib.cpp
+++ b/clang/lib/Driver/Multilib.cpp
@@ -11,6 +11,7 @@
 #include "clang/Driver/Driver.h"
 #include "llvm/ADT/DenseSet.h"
 #include "llvm/ADT/SmallSet.h"
+#include "llvm/ADT/SmallString.h"
 #include "llvm/ADT/StringRef.h"
 #include "llvm/Support/Compiler.h"
 #include "llvm/Support/ErrorHandling.h"
@@ -92,12 +93,141 @@ MultilibSet &MultilibSet::FilterOut(FilterCallback F) {
 
 void MultilibSet::push_back(const Multilib &M) { Multilibs.push_back(M); }
 
-bool MultilibSet::select(const Driver &D, const Multilib::flags_list &Flags,
-                         llvm::SmallVectorImpl<Multilib> &Selected) const {
-  llvm::StringSet<> FlagSet(expandFlags(Flags));
+static void DiagnoseUnclaimedMultilibCustomFlags(
+    const Driver &D, const SmallVector<StringRef> &UnclaimedCustomFlagValues,
+    const SmallVector<custom_flag::DeclarationPtr> &CustomFlagDecls) {
+  struct EditDistanceInfo {
+    StringRef FlagValue;
+    unsigned EditDistance;
+  };
+  const unsigned MaxEditDistance = 5;
+
+  for (StringRef Unclaimed : UnclaimedCustomFlagValues) {
+    std::optional<EditDistanceInfo> BestCandidate;
+    for (const auto &Decl : CustomFlagDecls) {
+      for (const auto &Value : Decl->ValueList) {
+        const std::string &FlagValueName = Value.Name;
+        unsigned EditDistance =
+            Unclaimed.edit_distance(FlagValueName, /*AllowReplacements=*/true,
+                                    /*MaxEditDistance=*/MaxEditDistance);
+        if (!BestCandidate || (EditDistance <= MaxEditDistance &&
+                               EditDistance < BestCandidate->EditDistance)) {
+          BestCandidate = {FlagValueName, EditDistance};
+        }
+      }
+    }
+    if (!BestCandidate)
+      D.Diag(clang::diag::err_drv_unsupported_opt)
+          << (custom_flag::Prefix + Unclaimed).str();
+    else
+      D.Diag(clang::diag::err_drv_unsupported_opt_with_suggestion)
+          << (custom_flag::Prefix + Unclaimed).str()
+          << (custom_flag::Prefix + BestCandidate->FlagValue).str();
+  }
+}
+
+namespace clang::driver::custom_flag {
+// Map implemented using linear searches as the expected size is too small for
+// the overhead of a search tree or a hash table.
+class ValueNameToDetailMap {
+  SmallVector<std::pair<StringRef, const ValueDetail *>> Mapping;
+
+public:
+  template <typename It>
+  ValueNameToDetailMap(It FlagDeclsBegin, It FlagDeclsEnd) {
+    for (auto DeclIt = FlagDeclsBegin; DeclIt != FlagDeclsEnd; ++DeclIt) {
+      const DeclarationPtr &Decl = *DeclIt;
+      for (const auto &Value : Decl->ValueList)
+        Mapping.emplace_back(Value.Name, &Value);
+    }
+  }
+
+  const ValueDetail *get(StringRef Key) const {
+    auto Iter = llvm::find_if(
+        Mapping, [&](const auto &Pair) { return Pair.first == Key; });
+    return Iter != Mapping.end() ? Iter->second : nullptr;
+  }
+};
+} // namespace clang::driver::custom_flag
+
+std::pair<Multilib::flags_list, SmallVector<StringRef>>
+MultilibSet::processCustomFlags(const Driver &D,
+                                const Multilib::flags_list &Flags) const {
+  Multilib::flags_list Result;
+  SmallVector<StringRef> CompilationArgs;
+
+  // Custom flag values detected in the flags list
+  SmallVector<const custom_flag::ValueDetail *> ClaimedCustomFlagValues;
+
+  // Arguments to -fmultilib-flag=<arg> that don't correspond to any valid
+  // custom flag value. An error will be printed out for each of these.
+  SmallVector<StringRef> UnclaimedCustomFlagValueStrs;
+
+  const auto ValueNameToValueDetail = custom_flag::ValueNameToDetailMap(
+      CustomFlagDecls.begin(), CustomFlagDecls.end());
+
+  for (StringRef Flag : Flags) {
+    if (!Flag.starts_with(custom_flag::Prefix)) {
+      Result.push_back(Flag.str());
+      continue;
+    }
+
+    StringRef CustomFlagValueStr = Flag.substr(custom_flag::Prefix.size());
+    const custom_flag::ValueDetail *Detail =
+        ValueNameToValueDetail.get(CustomFlagValueStr);
+    if (Detail)
+      ClaimedCustomFlagValues.push_back(Detail);
+    else
+      UnclaimedCustomFlagValueStrs.push_back(CustomFlagValueStr);
+  }
+
+  // Set of custom flag declarations for which a value was passed in the flags
+  // list. This is used to, firstly, detect multiple values for the same flag
+  // declaration (in this case, the last one wins), and secondly, to detect
+  // which declarations had no value passed in (in this case, the default value
+  // is selected).
+  llvm::SmallSet<custom_flag::DeclarationPtr, 32> TriggeredCustomFlagDecls;
+
+  // Detect multiple values for the same flag declaration. Last one wins.
+  for (auto *CustomFlagValue : llvm::reverse(ClaimedCustomFlagValues)) {
+    if (!TriggeredCustomFlagDecls.insert(CustomFlagValue->Decl).second)
+      continue;
+    Result.push_back(std::string(custom_flag::Prefix) + CustomFlagValue->Name);
+    if (CustomFlagValue->DriverArgs)
+      CompilationArgs.append(CustomFlagValue->DriverArgs->begin(),
+                             CustomFlagValue->DriverArgs->end());
+  }
+
+  // Detect flag declarations with no value passed in. Select default value.
+  for (const auto &Decl : CustomFlagDecls) {
+    if (TriggeredCustomFlagDecls.contains(Decl))
+      continue;
+    custom_flag::ValueDetail &CustomFlagValue =
+        Decl->ValueList[*Decl->DefaultValueIdx];
+    Result.push_back(std::string(custom_flag::Prefix) + CustomFlagValue.Name);
+    if (CustomFlagValue.DriverArgs)
+      CompilationArgs.append(CustomFlagValue.DriverArgs->begin(),
+                             CustomFlagValue.DriverArgs->end());
+  }
+
+  DiagnoseUnclaimedMultilibCustomFlags(D, UnclaimedCustomFlagValueStrs,
+                                       CustomFlagDecls);
+
+  return {Result, CompilationArgs};
+}
+
+bool MultilibSet::select(
+    const Driver &D, const Multilib::flags_list &Flags,
+    llvm::SmallVectorImpl<Multilib> &Selected,
+    llvm::SmallVector<StringRef> *CustomFlagCompilationArgs) const {
+  auto [FlagsWithCustom, CFCompilationArgs] = processCustomFlags(D, Flags);
+  llvm::StringSet<> FlagSet(expandFlags(FlagsWithCustom));
   Selected.clear();
   bool AnyErrors = false;
 
+  if (CustomFlagCompilationArgs)
+    *CustomFlagCompilationArgs = std::move(CFCompilationArgs);
+
   // Decide which multilibs we're going to select at all.
   llvm::DenseSet<StringRef> ExclusiveGroupsSelected;
   for (const Multilib &M : llvm::reverse(Multilibs)) {
diff --git a/clang/lib/Driver/ToolChains/BareMetal.cpp b/clang/lib/Driver/ToolChains/BareMetal.cpp
index f9a73f60973e4c..c5711d94e26602 100644
--- a/clang/lib/Driver/ToolChains/BareMetal.cpp
+++ b/clang/lib/Driver/ToolChains/BareMetal.cpp
@@ -183,9 +183,11 @@ static bool isPPCBareMetal(const llvm::Triple &Triple) {
          Triple.getEnvironment() == llvm::Triple::EABI;
 }
 
-static void findMultilibsFromYAML(const ToolChain &TC, const Driver &D,
-                                  StringRef MultilibPath, const ArgList &Args,
-                                  DetectedMultilibs &Result) {
+static void
+findMultilibsFromYAML(const ToolChain &TC, const Driver &D,
+                      StringRef MultilibPath, const ArgList &Args,
+                      DetectedMultilibs &Result,
+                      SmallVector<StringRef> &CustomFlagsCompilationArgs) {
   llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>> MB =
       D.getVFS().getBufferForFile(MultilibPath);
   if (!MB)
@@ -196,7 +198,8 @@ static void findMultilibsFromYAML(const ToolChain &TC, const Driver &D,
   if (ErrorOrMultilibSet.getError())
     return;
   Result.Multilibs = ErrorOrMultilibSet.get();
-  if (Result.Multilibs.select(D, Flags, Result.SelectedMultilibs))
+  if (Result.Multilibs.select(D, Flags, Result.SelectedMultilibs,
+                              &CustomFlagsCompilationArgs))
     return;
   D.Diag(clang::diag::warn_drv_missing_multilib) << llvm::join(Flags, " ");
   std::stringstream ss;
@@ -255,9 +258,13 @@ void BareMetal::findMultilibs(const Driver &D, const llvm::Triple &Triple,
     // If multilib.yaml is found, update sysroot so it doesn't use a target
     // specific suffix
     SysRoot = computeBaseSysRoot(D, /*IncludeTriple=*/false);
-    findMultilibsFromYAML(*this, D, *MultilibPath, Args, Result);
+    SmallVector<StringRef> CustomFlagDriverArgs;
+    findMultilibsFromYAML(*this, D, *MultilibPath, Args, Result,
+                          CustomFlagDriverArgs);
     SelectedMultilibs = Result.SelectedMultilibs;
     Multilibs = Result.Multilibs;
+    MultilibDriverArgs.append(CustomFlagDriverArgs.begin(),
+                              CustomFlagDriverArgs.end());
   } else if (isRISCVBareMetal(Triple)) {
     if (findRISCVMultilibs(D, Triple, Args, Result)) {
       SelectedMultilibs = Result.SelectedMultilibs;
@@ -597,3 +604,8 @@ SanitizerMask BareMetal::getSupportedSanitizers() const {
   }
   return Res;
 }
+
+SmallVector<std::string>
+BareMetal::getMultilibDriverArgsStr(llvm::opt::ArgList &Args) const {
+  return MultilibDriverArgs;
+}
\ No newline at end of file
diff --git a/clang/lib/Driver/ToolChains/BareMetal.h b/clang/lib/Driver/ToolChains/BareMetal.h
index b385c8cf76aab0..eb37124bbdc315 100644
--- a/clang/lib/Driver/ToolChains/BareMetal.h
+++ b/clang/lib/Driver/ToolChains/BareMetal.h
@@ -74,12 +74,17 @@ class LLVM_LIBRARY_VISIBILITY BareMetal : public ToolChain {
   std::string computeSysRoot() const override;
   SanitizerMask getSupportedSanitizers() const override;
 
+  SmallVector<std::string>
+  getMultilibDriverArgsStr(llvm::opt::ArgList &Args) const override;
+
 private:
   using OrderedMultilibs =
       llvm::iterator_range<llvm::SmallVector<Multilib>::const_reverse_iterator>;
   OrderedMultilibs getOrderedMultilibs() const;
 
   std::string SysRoot;
+
+  SmallVector<std::string> MultilibDriverArgs;
 };
 
 } // namespace toolchains
diff --git a/clang/test/Driver/baremetal-multilib-custom-flags.yaml b/clang/test/Driver/baremetal-multilib-custom-flags.yaml
new file mode 100644
index 00000000000000..94f7838c3cd96c
--- /dev/null
+++ b/clang/test/Driver/baremetal-multilib-custom-flags.yaml
@@ -0,0 +1,79 @@
+# RUN: %clang --multi-lib-config=%s -no-canonical-prefixes -x c %s -### -o /dev/null 2>&1 \
+# RUN:     --target=thumbv8m.main-none-eabi -mfpu=none --sysroot= \
+# RUN:   | FileCheck --check-prefix=CHECK-DEFAULT %s
+
+# CHECK-DEFAULT:      "-cc1" "-triple" "thumbv8m.main-unknown-none-eabi"
+# CHECK-DEFAULT-SAME: "-internal-isystem" "[[SYSROOT:[^"]*]]/bin/../lib/clang-runtimes/arm-none-eabi/thumb/v8-m.main/nofp/include"
+# CHECK-DEFAULT-NEXT: "-L[[SYSROOT]]/bin/../lib/clang-runtimes/arm-none-eabi/thumb/v8-m.main/nofp/lib"
+
+# RUN: %clang --multi-lib-config=%s -no-canonical-prefixes -x c %s -### -o /dev/null 2>&1 \
+# RUN:     --target=thumbv8m.main-none-eabi -mfpu=none -fmultilib-flag=no-multithreaded --sysroot= \
+# RUN:   | FileCheck --check-prefix=CHECK-NOMULTI %s
+
+# CHECK-NOMULTI:      "-cc1" "-triple" "thumbv8m.main-unknown-none-eabi"
+# CHECK-NOMULTI-SAME: "-internal-isystem" "[[SYSROOT:[^"]*]]/bin/../lib/clang-runtimes/arm-none-eabi/thumb/v8-m.main/nofp/include"
+# CHECK-NOMULTI-NEXT: "-L[[SYSROOT]]/bin/../lib/clang-runtimes/arm-none-eabi/thumb/v8-m.main/nofp/lib"
+
+# RUN: %clang --multi-lib-config=%s -no-canonical-prefixes -x c %s -### -o /dev/null 2>&1 \
+# RUN:     --target=thumbv8m.main-none-eabi -mfpu=none -fmultilib-flag=multithreaded --sysroot= \
+# RUN:   | FileCheck --check-prefix=CHECK-MULTI %s
+
+# CHECK-MULTI:      "-cc1" "-triple" "thumbv8m.main-unknown-none-eabi"
+# CHECK-MULTI-SAME: "-internal-isystem" "[[SYSROOT:[^"]*]]/bin/../lib/clang-runtimes/arm-none-eabi/multithreaded/thumb/v8-m.main/nofp/include"
+# CHECK-MULTI-NEXT: "-L[[SYSROOT]]/bin/../lib/clang-runtimes/arm-none-eabi/multithreaded/thumb/v8-m.main/nofp/lib"
+
+# RUN: not %clang --multi-lib-config=%s -no-canonical-prefixes -x c %s -### -o /dev/null 2>&1 \
+# RUN:     --target=thumbv8m.main-none-eabi -mfpu=none -fmultilib-flag=singlethreaded -fmultilib-flag=no-io --sysroot= \
+# RUN:   | FileCheck --check-prefix=CHECK-ERROR %s
+# CHECK-ERROR-DAG: error: unsupported option '-fmultilib-flag=singlethreaded'
+# CHECK-ERROR-DAG: error: unsupported option '-fmultilib-flag=no-io'; did you mean '-fmultilib-flag=io-none'?
+
+# RUN: %clang --multi-lib-config=%s -no-canonical-prefixes -x c %s -### -o /dev/null 2>&1 \
+# RUN:     --target=thumbv8m.main-none-eabi -mfpu=none -print-multi-lib --sysroot= \
+# RUN:   | FileCheck --check-prefix=CHECK-PRINT-MULTI-LIB %s
+# CHECK-PRINT-MULTI-LIB: arm-none-eabi/thumb/v8-m.main/nofp;@-target=thumbv8m.main-unknown-none-eabi at mfpu=none at fmultilib-flag=no-multithreaded
+# CHECK-PRINT-MULTI-LIB: arm-none-eabi/multithreaded/thumb/v8-m.main/nofp;@-target=thumbv8m.main-unknown-none-eabi at mfpu=none at fmultilib-flag=multithreaded
+
+# RUN: %clang --target=arm-none-eabi --multi-lib-config=%s -x c %s -fmultilib-flag=no-multithreaded -### -o /dev/null 2>&1 \
+# RUN: | FileCheck --check-prefix=CHECK-DRIVERARGS-NOMULTI %s
+# CHECK-DRIVERARGS-NOMULTI:        "-D" "__SINGLE_THREAD__"
+
+# RUN: %clang --target=arm-none-eabi --multi-lib-config=%s -x c %s -fmultilib-flag=io-semihosting -### -o /dev/null 2>&1 \
+# RUN: | FileCheck --check-prefix=CHECK-DRIVERARGS-IO-SEMIHOSTING %s
+# CHECK-DRIVERARGS-IO-SEMIHOSTING: "-D" "SEMIHOSTING"
+
+# RUN: %clang --target=arm-none-eabi --multi-lib-config=%s -x c %s -fmultilib-flag=io-linux-syscalls -### -o /dev/null 2>&1 \
+# RUN: | FileCheck --check-prefix=CHECK-DRIVERARGS-IO-LINUX %s
+# CHECK-DRIVERARGS-IO-LINUX:       "-U" "SEMIHOSTING"
+# CHECK-DRIVERARGS-IO-LINUX-SAME:  "-D" "LINUX_SYSCALLS"
+
+---
+MultilibVersion: 1.0
+
+Groups:
+- Name: stdlib
+  Type: Exclusive
+
+Variants:
+- Dir: arm-none-eabi/thumb/v8-m.main/nofp
+  Flags: [--target=thumbv8m.main-unknown-none-eabi, -mfpu=none, -fmultilib-flag=no-multithreaded]
+  Group: stdlib
+- Dir: arm-none-eabi/multithreaded/thumb/v8-m.main/nofp
+  Flags: [--target=thumbv8m.main-unknown-none-eabi, -mfpu=none, -fmultilib-flag=multithreaded]
+  Group: stdlib
+
+Flags:
+  - Name: multithreading
+    Values:
+    - Name: no-multithreaded
+      DriverArgs: [-D__SINGLE_THREAD__]
+    - Name: multithreaded
+    Default: no-multithreaded
+  - Name: io
+    Values:
+    - Name: io-none
+    - Name: io-semihosting
+      DriverArgs: [-DSEMIHOSTING]
+    - Name: io-linux-syscalls
+      DriverArgs: [-USEMIHOSTING, -DLINUX_SYSCALLS]
+    Default: io-none
\ No newline at end of file



More information about the llvm-branch-commits mailing list