michaelplatings updated this revision to Diff 499443.
michaelplatings added a subscriber: peter.smith.
michaelplatings added a comment.

- Squash D143763 <https://reviews.llvm.org/D143763> into this change as 
suggested by @MaskRay
- Use PascalCase field names as requested by @phosek
- Applied improvements to error messages suggested by @peter.smith
- Removed some braces as requested by @MaskRay
- Moved serialization structs to anonymous namespace as requested by @MaskRay


Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D142932/new/

https://reviews.llvm.org/D142932

Files:
  clang/include/clang/Driver/Multilib.h
  clang/lib/Driver/Multilib.cpp
  clang/unittests/Driver/MultilibTest.cpp

Index: clang/unittests/Driver/MultilibTest.cpp
===================================================================
--- clang/unittests/Driver/MultilibTest.cpp
+++ clang/unittests/Driver/MultilibTest.cpp
@@ -13,6 +13,7 @@
 #include "clang/Driver/Multilib.h"
 #include "../../lib/Driver/ToolChains/CommonArgs.h"
 #include "clang/Basic/LLVM.h"
+#include "clang/Basic/Version.h"
 #include "llvm/ADT/ArrayRef.h"
 #include "llvm/ADT/StringRef.h"
 #include "llvm/ADT/StringSwitch.h"
@@ -187,3 +188,408 @@
   EXPECT_EQ("/a", Selection[0].gccSuffix());
   EXPECT_EQ("/b", Selection[1].gccSuffix());
 }
+
+static void diagnosticCallback(const llvm::SMDiagnostic &D, void *Out) {
+  *reinterpret_cast<std::string *>(Out) = D.getMessage();
+}
+
+static bool parse(MultilibSet &MS, std::string &Diagnostic, const char *Data) {
+  return MS.parse(llvm::MemoryBufferRef(Data, "TEST"), diagnosticCallback,
+                  &Diagnostic);
+}
+
+static bool parse(MultilibSet &MS, std::string &Diagnostic,
+                  const std::string &Data) {
+  return MS.parse(llvm::MemoryBufferRef(Data, "TEST"), diagnosticCallback,
+                  &Diagnostic);
+}
+
+static bool parse(MultilibSet &MS, const char *Data) {
+  return MS.parse(llvm::MemoryBufferRef(Data, "TEST"));
+}
+
+#define _STRINGIFY(x) #x
+#define STRINGIFY(x) _STRINGIFY(x)
+// Avoid using MULTILIB_CLANG_VERSION in case it has extra non-numeric parts.
+#define MULTILIB_CLANG_VERSION                                                 \
+  STRINGIFY(CLANG_VERSION_MAJOR)                                               \
+  "." STRINGIFY(CLANG_VERSION_MINOR) "." STRINGIFY(CLANG_VERSION_PATCHLEVEL)
+#define YAML_PREAMBLE "ClangMinimumVersion: " MULTILIB_CLANG_VERSION "\n"
+
+TEST(MultilibTest, ParseInvalid) {
+  std::string Diagnostic;
+
+  MultilibSet MS;
+
+  EXPECT_FALSE(parse(MS, Diagnostic, R"(
+Variants: []
+)"));
+  EXPECT_TRUE(StringRef(Diagnostic)
+                  .contains("missing required key 'ClangMinimumVersion'"))
+      << Diagnostic;
+
+  // Require all 3 major.minor.patch version components
+  EXPECT_FALSE(parse(MS, Diagnostic, R"(
+ClangMinimumVersion: )" STRINGIFY(CLANG_VERSION_MAJOR) R"(.0
+Variants: []
+)"));
+  EXPECT_TRUE(StringRef(Diagnostic)
+                  .contains("not a valid version string. Expected "
+                            "MAJOR.MINOR.PATCHLEVEL but got \"" STRINGIFY(
+                                CLANG_VERSION_MAJOR) ".0\""))
+      << Diagnostic;
+
+  EXPECT_FALSE(parse(MS, Diagnostic, R"(
+ClangMinimumVersion: )" MULTILIB_CLANG_VERSION R"(a
+Variants: []
+)"));
+  EXPECT_TRUE(
+      StringRef(Diagnostic)
+          .contains("not a valid version string. Expected "
+                    "MAJOR.MINOR.PATCHLEVEL where all components are decimal "
+                    "integers but got \"" MULTILIB_CLANG_VERSION "a\""))
+      << Diagnostic;
+
+  // Reject configurations that require a later clang version
+  EXPECT_FALSE(parse(MS, Diagnostic,
+                     R"(
+ClangMinimumVersion: )" + std::to_string(CLANG_VERSION_MAJOR + 1) +
+                         R"(.0.0
+Variants: []
+)"));
+  EXPECT_TRUE(StringRef(Diagnostic)
+                  .contains("clang version " MULTILIB_CLANG_VERSION
+                            " is less than ClangMinimumVersion: " +
+                            std::to_string(CLANG_VERSION_MAJOR + 1) + ".0.0"))
+      << Diagnostic;
+
+  // but accept configurations that only need an earlier clang version
+  EXPECT_TRUE(parse(MS, Diagnostic, R"(
+ClangMinimumVersion: 16.0.0
+Variants: []
+)")) << Diagnostic;
+
+  EXPECT_FALSE(parse(MS, Diagnostic, YAML_PREAMBLE));
+  EXPECT_TRUE(StringRef(Diagnostic).contains("missing required key 'Variants'"))
+      << Diagnostic;
+
+  EXPECT_FALSE(parse(MS, Diagnostic, YAML_PREAMBLE R"(
+Variants:
+- Dir: /abc
+  Flags: []
+  PrintArgs: []
+)"));
+  EXPECT_TRUE(StringRef(Diagnostic).contains("paths must be relative"))
+      << Diagnostic;
+
+  EXPECT_FALSE(parse(MS, Diagnostic, YAML_PREAMBLE R"(
+Variants:
+- Flags: []
+  PrintArgs: []
+)"));
+  EXPECT_TRUE(StringRef(Diagnostic).contains("missing required key 'Dir'"))
+      << Diagnostic;
+
+  EXPECT_FALSE(parse(MS, Diagnostic, YAML_PREAMBLE R"(
+Variants:
+- Dir: .
+  PrintArgs: []
+)"));
+  EXPECT_TRUE(StringRef(Diagnostic).contains("missing required key 'Flags'"))
+      << Diagnostic;
+
+  EXPECT_FALSE(parse(MS, Diagnostic, YAML_PREAMBLE R"(
+Variants:
+- Dir: .
+  Flags: []
+)"));
+  EXPECT_TRUE(
+      StringRef(Diagnostic).contains("missing required key 'PrintArgs'"))
+      << Diagnostic;
+
+  EXPECT_FALSE(parse(MS, Diagnostic, YAML_PREAMBLE R"(
+Variants: []
+FlagMap:
+- Regex: abc
+)"));
+  EXPECT_TRUE(
+      StringRef(Diagnostic)
+          .contains("value required for 'MatchFlags' or 'NoMatchFlags'"))
+      << Diagnostic;
+
+  EXPECT_FALSE(parse(MS, Diagnostic, YAML_PREAMBLE R"(
+Variants: []
+FlagMap:
+- Dir: .
+  Regex: '('
+  PrintArgs: []
+)"));
+  EXPECT_TRUE(StringRef(Diagnostic).contains("parentheses not balanced"))
+      << Diagnostic;
+}
+
+TEST(MultilibTest, Parse) {
+  MultilibSet MS;
+  EXPECT_TRUE(parse(MS, YAML_PREAMBLE R"(
+Variants:
+- Dir: .
+  Flags: []
+  PrintArgs: []
+)"));
+  EXPECT_EQ(1U, MS.size());
+  EXPECT_EQ("", MS.begin()->gccSuffix());
+
+  EXPECT_TRUE(parse(MS, YAML_PREAMBLE R"(
+Variants:
+- Dir: abc
+  Flags: []
+  PrintArgs: []
+)"));
+  EXPECT_EQ(1U, MS.size());
+  EXPECT_EQ("/abc", MS.begin()->gccSuffix());
+
+  EXPECT_TRUE(parse(MS, YAML_PREAMBLE R"(
+Variants:
+- Dir: pqr
+  Flags: []
+  PrintArgs: [-mfloat-abi=soft]
+)"));
+  EXPECT_EQ(1U, MS.size());
+  EXPECT_EQ("/pqr", MS.begin()->gccSuffix());
+  EXPECT_EQ(std::vector<std::string>({"-mfloat-abi=soft"}),
+            MS.begin()->getPrintArgs());
+
+  EXPECT_TRUE(parse(MS, YAML_PREAMBLE R"(
+Variants:
+- Dir: pqr
+  Flags: []
+  PrintArgs: [-mfloat-abi=soft, -fno-exceptions]
+)"));
+  EXPECT_EQ(1U, MS.size());
+  EXPECT_EQ(std::vector<std::string>({"-mfloat-abi=soft", "-fno-exceptions"}),
+            MS.begin()->getPrintArgs());
+
+  EXPECT_TRUE(parse(MS, YAML_PREAMBLE R"(
+Variants:
+- Dir: a
+  Flags: []
+  PrintArgs: []
+- Dir: b
+  Flags: []
+  PrintArgs: []
+)"));
+  EXPECT_EQ(2U, MS.size());
+}
+
+TEST(MultilibTest, SelectSoft) {
+  MultilibSet MS;
+  Multilib Selected;
+  ASSERT_TRUE(parse(MS, YAML_PREAMBLE R"(
+Variants:
+- Dir: s
+  Flags: [softabi]
+  PrintArgs: []
+FlagMap:
+- Regex: mfloat-abi=soft
+  MatchFlags: [softabi]
+- Regex: mfloat-abi=softfp
+  MatchFlags: [softabi]
+)"));
+  EXPECT_TRUE(MS.select({"mfloat-abi=soft"}, Selected));
+  EXPECT_TRUE(MS.select({"mfloat-abi=softfp"}, Selected));
+  EXPECT_FALSE(MS.select({"mfloat-abi=hard"}, Selected));
+}
+
+TEST(MultilibTest, SelectSoftFP) {
+  MultilibSet MS;
+  Multilib Selected;
+  ASSERT_TRUE(parse(MS, YAML_PREAMBLE R"(
+Variants:
+- Dir: f
+  Flags: [mfloat-abi=softfp]
+  PrintArgs: []
+)"));
+  EXPECT_FALSE(MS.select({"mfloat-abi=soft"}, Selected));
+  EXPECT_TRUE(MS.select({"mfloat-abi=softfp"}, Selected));
+  EXPECT_FALSE(MS.select({"mfloat-abi=hard"}, Selected));
+}
+
+TEST(MultilibTest, SelectHard) {
+  // If hard float is all that's available then select that only if compiling
+  // with hard float.
+  MultilibSet MS;
+  Multilib Selected;
+  ASSERT_TRUE(parse(MS, YAML_PREAMBLE R"(
+Variants:
+- Dir: h
+  Flags: [mfloat-abi=hard]
+  PrintArgs: []
+)"));
+  EXPECT_FALSE(MS.select({"mfloat-abi=soft"}, Selected));
+  EXPECT_FALSE(MS.select({"mfloat-abi=softfp"}, Selected));
+  EXPECT_TRUE(MS.select({"mfloat-abi=hard"}, Selected));
+}
+
+TEST(MultilibTest, SelectFloatABI) {
+  MultilibSet MS;
+  Multilib Selected;
+  ASSERT_TRUE(parse(MS, YAML_PREAMBLE R"(
+Variants:
+- Dir: s
+  Flags: [softabi]
+  PrintArgs: []
+- Dir: f
+  Flags: [softabi, hasfp]
+  PrintArgs: []
+- Dir: h
+  Flags: [hardabi, hasfp]
+  PrintArgs: []
+FlagMap:
+- Regex: mfloat-abi=(soft|softfp)
+  MatchFlags: [softabi]
+- Regex: mfloat-abi=hard
+  MatchFlags: [hardabi]
+- Regex: mfloat-abi=soft
+  NoMatchFlags: [hasfp]
+)"));
+  MS.select({"mfloat-abi=soft"}, Selected);
+  EXPECT_EQ("/s", Selected.gccSuffix());
+  MS.select({"mfloat-abi=softfp"}, Selected);
+  EXPECT_EQ("/f", Selected.gccSuffix());
+  MS.select({"mfloat-abi=hard"}, Selected);
+  EXPECT_EQ("/h", Selected.gccSuffix());
+}
+
+TEST(MultilibTest, SelectFloatABIReversed) {
+  // If soft is specified after softfp then softfp will never be
+  // selected because soft is compatible with softfp and last wins.
+  MultilibSet MS;
+  Multilib Selected;
+  ASSERT_TRUE(parse(MS, YAML_PREAMBLE R"(
+Variants:
+- Dir: h
+  Flags: [hardabi, hasfp]
+  PrintArgs: []
+- Dir: f
+  Flags: [softabi, hasfp]
+  PrintArgs: []
+- Dir: s
+  Flags: [softabi]
+  PrintArgs: []
+FlagMap:
+- Regex: mfloat-abi=(soft|softfp)
+  MatchFlags: [softabi]
+- Regex: mfloat-abi=hard
+  MatchFlags: [hardabi]
+- Regex: mfloat-abi=soft
+  NoMatchFlags: [hasfp]
+)"));
+  MS.select({"mfloat-abi=soft"}, Selected);
+  EXPECT_EQ("/s", Selected.gccSuffix());
+  MS.select({"mfloat-abi=softfp"}, Selected);
+  EXPECT_EQ("/s", Selected.gccSuffix());
+  MS.select({"mfloat-abi=hard"}, Selected);
+  EXPECT_EQ("/h", Selected.gccSuffix());
+}
+
+TEST(MultilibTest, SelectMClass) {
+  const char *MultilibSpec = YAML_PREAMBLE R"(
+Variants:
+- Dir: thumb/v6-m/nofp
+  Flags: [target=thumbv6m-none-eabi]
+  PrintArgs: [--target=thumbv6m-none-eabi, -mfloat-abi=soft]
+
+- Dir: thumb/v7-m/nofp
+  Flags: [target=thumbv7m-none-eabi]
+  PrintArgs: [--target=thumbv7m-none-eabi, -mfloat-abi=soft]
+
+- Dir: thumb/v7e-m/nofp
+  Flags: [target=thumbv7em-none-eabi]
+  PrintArgs: [--target=thumbv7em-none-eabi, -mfloat-abi=soft, -mfpu=none]
+
+- Dir: thumb/v8-m.main/nofp
+  Flags: [target=thumbv8m.main-none-eabi]
+  PrintArgs: [--target=arm-none-eabi, -mfloat-abi=soft, -march=armv8m.main+nofp]
+
+- Dir: thumb/v8.1-m.main/nofp/nomve
+  Flags: [target=thumbv8.1m.main-none-eabi]
+  PrintArgs: [--target=arm-none-eabi, -mfloat-abi=soft, -march=armv8.1m.main+nofp+nomve]
+
+- Dir: thumb/v7e-m/fpv4_sp_d16
+  Flags: [target=thumbv7em-none-eabihf, mfpu=fpv4-sp-d16]
+  PrintArgs: [--target=thumbv7em-none-eabihf, -mfpu=fpv4-sp-d16]
+
+- Dir: thumb/v7e-m/fpv5_d16
+  Flags: [target=thumbv7em-none-eabihf, mfpu=fpv5-d16]
+  PrintArgs: [--target=thumbv7em-none-eabihf, -mfpu=fpv5-d16]
+
+- Dir: thumb/v8-m.main/fp
+  Flags: [target=thumbv8m.main-none-eabihf]
+  PrintArgs: [--target=thumbv8m.main-none-eabihf]
+
+- Dir: thumb/v8.1-m.main/fp
+  Flags: [target=thumbv8.1m.main-none-eabihf]
+  PrintArgs: [--target=thumbv8.1m.main-none-eabihf]
+
+- Dir: thumb/v8.1-m.main/nofp/mve
+  Flags: [target=thumbv8.1m.main-none-eabihf, march=+mve]
+  PrintArgs: [--target=arm-none-eabihf, -march=armv8.1m.main+nofp+mve]
+
+FlagMap:
+- Regex: target=thumbv8(\.[0-9]+)?m\.base-none-eabi
+  MatchFlags: [target=thumbv6m-none-eabi]
+- Regex: thumbv8\.[1-9]m\.main-none-eabi
+  MatchFlags: [target=thumbv8.1m.main-none-eabi]
+- Regex: thumbv8\.[1-9]m\.main-none-eabihf
+  MatchFlags: [target=thumbv8.1m.main-none-eabihf]
+)";
+
+  MultilibSet MS;
+  Multilib Selected;
+  ASSERT_TRUE(parse(MS, MultilibSpec));
+
+  EXPECT_TRUE(
+      MS.select({"target=thumbv6m-none-eabi", "mfloat-abi=soft"}, Selected));
+  EXPECT_EQ("/thumb/v6-m/nofp", Selected.gccSuffix());
+
+  EXPECT_TRUE(
+      MS.select({"target=thumbv7m-none-eabi", "mfloat-abi=soft"}, Selected));
+  EXPECT_EQ("/thumb/v7-m/nofp", Selected.gccSuffix());
+
+  EXPECT_TRUE(
+      MS.select({"target=thumbv7em-none-eabi", "mfloat-abi=soft", "mfpu=none"},
+                Selected));
+  EXPECT_EQ("/thumb/v7e-m/nofp", Selected.gccSuffix());
+
+  EXPECT_TRUE(MS.select({"target=thumbv8m.main-none-eabi", "mfloat-abi=soft"},
+                        Selected));
+  EXPECT_EQ("/thumb/v8-m.main/nofp", Selected.gccSuffix());
+
+  EXPECT_TRUE(MS.select(
+      {"target=thumbv8.1m.main-none-eabi", "mfloat-abi=soft", "mfpu=none"},
+      Selected));
+  EXPECT_EQ("/thumb/v8.1-m.main/nofp/nomve", Selected.gccSuffix());
+
+  EXPECT_TRUE(MS.select(
+      {"target=thumbv7em-none-eabihf", "mfloat-abi=hard", "mfpu=fpv4-sp-d16"},
+      Selected));
+  EXPECT_EQ("/thumb/v7e-m/fpv4_sp_d16", Selected.gccSuffix());
+
+  EXPECT_TRUE(MS.select(
+      {"target=thumbv7em-none-eabihf", "mfloat-abi=hard", "mfpu=fpv5-d16"},
+      Selected));
+  EXPECT_EQ("/thumb/v7e-m/fpv5_d16", Selected.gccSuffix());
+
+  EXPECT_TRUE(MS.select({"target=thumbv8m.main-none-eabihf", "mfloat-abi=hard"},
+                        Selected));
+  EXPECT_EQ("/thumb/v8-m.main/fp", Selected.gccSuffix());
+
+  EXPECT_TRUE(MS.select(
+      {"target=thumbv8.1m.main-none-eabihf", "mfloat-abi=hard"}, Selected));
+  EXPECT_EQ("/thumb/v8.1-m.main/fp", Selected.gccSuffix());
+
+  EXPECT_TRUE(MS.select({"target=thumbv8.1m.main-none-eabihf",
+                         "mfloat-abi=hard", "mfpu=none", "march=+mve"},
+                        Selected));
+  EXPECT_EQ("/thumb/v8.1-m.main/nofp/mve", Selected.gccSuffix());
+}
Index: clang/lib/Driver/Multilib.cpp
===================================================================
--- clang/lib/Driver/Multilib.cpp
+++ clang/lib/Driver/Multilib.cpp
@@ -8,6 +8,7 @@
 
 #include "clang/Driver/Multilib.h"
 #include "clang/Basic/LLVM.h"
+#include "clang/Basic/Version.h"
 #include "llvm/ADT/SmallString.h"
 #include "llvm/ADT/StringMap.h"
 #include "llvm/ADT/StringRef.h"
@@ -16,10 +17,10 @@
 #include "llvm/Support/ErrorHandling.h"
 #include "llvm/Support/Path.h"
 #include "llvm/Support/Regex.h"
+#include "llvm/Support/YAMLParser.h"
+#include "llvm/Support/YAMLTraits.h"
 #include "llvm/Support/raw_ostream.h"
-#include <algorithm>
 #include <cassert>
-#include <string>
 
 using namespace clang;
 using namespace driver;
@@ -80,12 +81,13 @@
 
 void MultilibSet::push_back(const Multilib &M) { Multilibs.push_back(M); }
 
-MultilibSet::multilib_list
+std::vector<Multilib>
 MultilibSet::select(const Multilib::flag_set &Flags) const {
+  Multilib::flag_set AllFlags(expandFlags(Flags));
   multilib_list Result;
   llvm::copy_if(Multilibs, std::back_inserter(Result),
-                [&Flags](const Multilib &M) {
-                  return std::includes(Flags.begin(), Flags.end(),
+                [&AllFlags](const Multilib &M) {
+                  return std::includes(AllFlags.begin(), AllFlags.end(),
                                        M.flags().begin(), M.flags().end());
                 });
   return Result;
@@ -100,6 +102,143 @@
   return true;
 }
 
+Multilib::flag_set
+MultilibSet::expandFlags(const Multilib::flag_set &InFlags) const {
+  Multilib::flag_set Result(InFlags);
+  for (const FlagMatcher &M : FlagMatchers) {
+    std::string RegexString(M.Regex);
+
+    // Make the regular expression match the whole string.
+    if (!StringRef(M.Regex).starts_with("^"))
+      RegexString.insert(RegexString.begin(), '^');
+    if (!StringRef(M.Regex).ends_with("$"))
+      RegexString.push_back('$');
+
+    const llvm::Regex Regex(RegexString);
+    assert(Regex.isValid());
+    if (llvm::find_if(InFlags, [&Regex](StringRef F) {
+          return Regex.match(F);
+        }) != InFlags.end()) {
+      Result.insert(M.MatchFlags.begin(), M.MatchFlags.end());
+    } else {
+      Result.insert(M.NoMatchFlags.begin(), M.NoMatchFlags.end());
+    }
+  }
+  return Result;
+}
+
+namespace {
+
+struct MultilibSerialization {
+  std::string Dir;
+  std::vector<std::string> Flags, PrintArgs;
+};
+
+struct MultilibSetSerialization {
+  std::string ClangMinimumVersion;
+  std::vector<MultilibSerialization> Multilibs;
+  std::vector<MultilibSet::FlagMatcher> FlagMatchers;
+};
+
+} // end anonymous namespace
+
+template <> struct llvm::yaml::MappingTraits<MultilibSerialization> {
+  static void mapping(llvm::yaml::IO &io, MultilibSerialization &V) {
+    io.mapRequired("Dir", V.Dir);
+    io.mapRequired("Flags", V.Flags);
+    io.mapRequired("PrintArgs", V.PrintArgs);
+  }
+  static std::string validate(IO &io, MultilibSerialization &V) {
+    if (StringRef(V.Dir).starts_with("/"))
+      return "paths must be relative. \"" + V.Dir + "\" starts with \"/\"\n";
+    return std::string{};
+  }
+};
+
+template <> struct llvm::yaml::MappingTraits<MultilibSet::FlagMatcher> {
+  static void mapping(llvm::yaml::IO &io, MultilibSet::FlagMatcher &M) {
+    io.mapRequired("Regex", M.Regex);
+    io.mapOptional("MatchFlags", M.MatchFlags);
+    io.mapOptional("NoMatchFlags", M.NoMatchFlags);
+  }
+  static std::string validate(IO &io, MultilibSet::FlagMatcher &M) {
+    llvm::Regex Regex(M.Regex);
+    std::string RegexError;
+    if (!Regex.isValid(RegexError))
+      return RegexError;
+    if (M.MatchFlags.empty() && M.NoMatchFlags.empty())
+      return "value required for 'MatchFlags' or 'NoMatchFlags'";
+    return std::string{};
+  }
+};
+
+template <> struct llvm::yaml::MappingTraits<MultilibSetSerialization> {
+  static void mapping(llvm::yaml::IO &io, MultilibSetSerialization &M) {
+    io.mapRequired("ClangMinimumVersion", M.ClangMinimumVersion);
+    io.mapRequired("Variants", M.Multilibs);
+    io.mapOptional("FlagMap", M.FlagMatchers);
+  }
+  static std::string validate(IO &io, MultilibSetSerialization &M) {
+    if (M.ClangMinimumVersion.empty())
+      return "missing required key 'ClangMinimumVersion'. Expected "
+             "MAJOR.MINOR.PATCHLEVEL";
+
+    SmallVector<int, 3> ClangMinimumVersion,
+        ClangVersion = {CLANG_VERSION_MAJOR, CLANG_VERSION_MINOR,
+                        CLANG_VERSION_PATCHLEVEL};
+
+    SmallVector<StringRef, 3> MinVerStrings;
+    StringRef(M.ClangMinimumVersion).split(MinVerStrings, '.');
+
+    if (MinVerStrings.size() != 3)
+      return "not a valid version string. Expected MAJOR.MINOR.PATCHLEVEL but "
+             "got \"" +
+             M.ClangMinimumVersion + "\"";
+
+    for (StringRef S : MinVerStrings) {
+      int V;
+      if (S.getAsInteger(10, V))
+        return "not a valid version string. Expected MAJOR.MINOR.PATCHLEVEL "
+               "where all components are decimal integers but got \"" +
+               M.ClangMinimumVersion + "\"";
+      ClangMinimumVersion.push_back(V);
+    }
+
+    if (ClangVersion < ClangMinimumVersion) {
+      return "clang version " CLANG_VERSION_STRING
+             " is less than ClangMinimumVersion: " +
+             M.ClangMinimumVersion;
+    }
+    return std::string{};
+  }
+};
+
+LLVM_YAML_IS_SEQUENCE_VECTOR(MultilibSerialization)
+LLVM_YAML_IS_SEQUENCE_VECTOR(MultilibSet::FlagMatcher)
+
+bool MultilibSet::parse(llvm::MemoryBufferRef Input,
+                        llvm::SourceMgr::DiagHandlerTy DiagHandler,
+                        void *DiagHandlerCtxt) {
+  MultilibSetSerialization MS;
+  llvm::yaml::Input YamlInput(Input, nullptr, DiagHandler, DiagHandlerCtxt);
+  YamlInput >> MS;
+  if (YamlInput.error())
+    return false;
+
+  Multilibs.clear();
+  Multilibs.reserve(MS.Multilibs.size());
+  for (const auto &M : MS.Multilibs) {
+    std::string Dir;
+    if (M.Dir != ".")
+      Dir = "/" + M.Dir;
+    Multilibs.emplace_back(Dir, Dir, Dir,
+                           Multilib::flag_set(M.Flags.begin(), M.Flags.end()),
+                           M.PrintArgs);
+  }
+  FlagMatchers = std::move(MS.FlagMatchers);
+  return true;
+}
+
 LLVM_DUMP_METHOD void MultilibSet::dump() const {
   print(llvm::errs());
 }
Index: clang/include/clang/Driver/Multilib.h
===================================================================
--- clang/include/clang/Driver/Multilib.h
+++ clang/include/clang/Driver/Multilib.h
@@ -14,6 +14,7 @@
 #include "llvm/ADT/STLExtras.h"
 #include "llvm/ADT/StringRef.h"
 #include "llvm/Support/Compiler.h"
+#include "llvm/Support/SourceMgr.h"
 #include <cassert>
 #include <functional>
 #include <set>
@@ -88,8 +89,19 @@
       std::function<std::vector<std::string>(const Multilib &M)>;
   using FilterCallback = llvm::function_ref<bool(const Multilib &)>;
 
+  /// Uses regular expressions to simplify flags used for multilib selection.
+  /// For example, we may wish to simplify armv8, armv8.1, armv8.2 etc. to
+  /// simply "v8". It's also possible to negate matches. For example, it may be
+  /// appropriate to infer that if mfpu=none *doesn't* match then an FPU is
+  /// available. NoMatchFlags can be used for this purpose.
+  struct FlagMatcher {
+    std::string Regex;
+    std::vector<std::string> MatchFlags, NoMatchFlags;
+  };
+
 private:
   multilib_list Multilibs;
+  std::vector<FlagMatcher> FlagMatchers;
   IncludeDirsFunc IncludeCallback;
   IncludeDirsFunc FilePathsCallback;
 
@@ -116,6 +128,15 @@
 
   unsigned size() const { return Multilibs.size(); }
 
+  /// Get the given flags plus flags found by matching them against the
+  /// FlagMatchers and choosing the MatchFlags or NoMatchFlags of each
+  /// accordingly. The select method calls this method so in most cases it's not
+  /// necessary to call it directly.
+  Multilib::flag_set expandFlags(const Multilib::flag_set &) const;
+
+  bool parse(llvm::MemoryBufferRef, llvm::SourceMgr::DiagHandlerTy = nullptr,
+             void *DiagHandlerCtxt = nullptr);
+
   LLVM_DUMP_METHOD void dump() const;
   void print(raw_ostream &OS) const;
 
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to