michaelplatings created this revision.
michaelplatings added a reviewer: phosek.
Herald added a project: All.
michaelplatings requested review of this revision.
Herald added subscribers: cfe-commits, MaskRay.
Herald added a project: clang.

Repository:
  rG LLVM Github Monorepo

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
@@ -187,3 +187,347 @@
   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, MultilibFlagMap &MFM,
+                  std::string &Diagnostic, const char *Data) {
+  return MS.parse(llvm::MemoryBufferRef(Data, "TEST"), MFM, diagnosticCallback,
+                  &Diagnostic);
+}
+
+static bool parse(MultilibSet &MS, MultilibFlagMap &MFM, const char *Data) {
+  return MS.parse(llvm::MemoryBufferRef(Data, "TEST"), MFM);
+}
+
+TEST(MultilibTest, ParseInvalid) {
+  std::string Diagnostic;
+
+  MultilibSet MS;
+  MultilibFlagMap MFM;
+  EXPECT_FALSE(parse(MS, MFM, Diagnostic, R"(
+variants:
+- dir: /abc
+  flags: []
+  printArgs: []
+)"));
+  EXPECT_TRUE(StringRef(Diagnostic).contains("paths must be relative"))
+      << Diagnostic;
+
+  EXPECT_FALSE(parse(MS, MFM, Diagnostic, R"(
+variants:
+- flags: []
+  printArgs: []
+)"));
+  EXPECT_TRUE(StringRef(Diagnostic).contains("missing required key 'dir'"))
+      << Diagnostic;
+
+  EXPECT_FALSE(parse(MS, MFM, Diagnostic, R"(
+variants:
+- dir: .
+  printArgs: []
+)"));
+  EXPECT_TRUE(StringRef(Diagnostic).contains("missing required key 'flags'"))
+      << Diagnostic;
+
+  EXPECT_FALSE(parse(MS, MFM, Diagnostic, R"(
+variants:
+- dir: .
+  flags: []
+)"));
+  EXPECT_TRUE(
+      StringRef(Diagnostic).contains("missing required key 'printArgs'"))
+      << Diagnostic;
+
+  EXPECT_FALSE(parse(MS, MFM, Diagnostic, R"(
+flagMap:
+- regex: abc
+)"));
+  EXPECT_TRUE(
+      StringRef(Diagnostic)
+          .contains("value required for 'matchFlags' or 'noMatchFlags'"))
+      << Diagnostic;
+}
+
+TEST(MultilibTest, Parse) {
+  MultilibSet MS;
+  MultilibFlagMap MFM;
+  EXPECT_TRUE(parse(MS, MFM, R"(
+variants:
+- dir: .
+  flags: []
+  printArgs: []
+)"));
+  EXPECT_EQ(1U, MS.size());
+  EXPECT_EQ("", MS.begin()->gccSuffix());
+
+  EXPECT_TRUE(parse(MS, MFM, R"(
+variants:
+- dir: abc
+  flags: []
+  printArgs: []
+)"));
+  EXPECT_EQ(1U, MS.size());
+  EXPECT_EQ("/abc", MS.begin()->gccSuffix());
+
+  EXPECT_TRUE(parse(MS, MFM, 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, MFM, 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, MFM, R"(
+variants:
+- dir: a
+  flags: []
+  printArgs: []
+- dir: b
+  flags: []
+  printArgs: []
+)"));
+  EXPECT_EQ(2U, MS.size());
+}
+
+static bool select(const std::vector<std::string> &InFlags,
+                   const MultilibSet &MS, const MultilibFlagMap &MFM,
+                   Multilib &Selected) {
+  Multilib::flags_list Flags(MFM.getFlags(InFlags));
+  Flags.insert(InFlags.begin(), InFlags.end());
+  return MS.select(Flags, Selected);
+}
+
+TEST(MultilibTest, SelectSoft) {
+  MultilibSet MS;
+  MultilibFlagMap MFM;
+  Multilib Selected;
+  ASSERT_TRUE(parse(MS, MFM, R"(
+variants:
+- dir: s
+  flags: [softabi]
+  printArgs: []
+flagMap:
+- regex: mfloat-abi=soft
+  matchFlags: [softabi]
+- regex: mfloat-abi=softfp
+  matchFlags: [softabi]
+)"));
+  EXPECT_TRUE(select({"mfloat-abi=soft"}, MS, MFM, Selected));
+  EXPECT_TRUE(select({"mfloat-abi=softfp"}, MS, MFM, Selected));
+  EXPECT_FALSE(select({"mfloat-abi=hard"}, MS, MFM, Selected));
+}
+
+TEST(MultilibTest, SelectSoftFP) {
+  MultilibSet MS;
+  MultilibFlagMap MFM;
+  Multilib Selected;
+  ASSERT_TRUE(parse(MS, MFM, R"(
+variants:
+- dir: f
+  flags: [mfloat-abi=softfp]
+  printArgs: []
+)"));
+  EXPECT_FALSE(select({"mfloat-abi=soft"}, MS, MFM, Selected));
+  EXPECT_TRUE(select({"mfloat-abi=softfp"}, MS, MFM, Selected));
+  EXPECT_FALSE(select({"mfloat-abi=hard"}, MS, MFM, Selected));
+}
+
+TEST(MultilibTest, SelectHard) {
+  // If hard float is all that's available then select that only if compiling
+  // with hard float.
+  MultilibSet MS;
+  MultilibFlagMap MFM;
+  Multilib Selected;
+  ASSERT_TRUE(parse(MS, MFM, R"(
+variants:
+- dir: h
+  flags: [mfloat-abi=hard]
+  printArgs: []
+)"));
+  EXPECT_FALSE(select({"mfloat-abi=soft"}, MS, MFM, Selected));
+  EXPECT_FALSE(select({"mfloat-abi=softfp"}, MS, MFM, Selected));
+  EXPECT_TRUE(select({"mfloat-abi=hard"}, MS, MFM, Selected));
+}
+
+TEST(MultilibTest, SelectFloatABI) {
+  MultilibSet MS;
+  MultilibFlagMap MFM;
+  Multilib Selected;
+  ASSERT_TRUE(parse(MS, MFM, 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]
+)"));
+  select({"mfloat-abi=soft"}, MS, MFM, Selected);
+  EXPECT_EQ("/s", Selected.gccSuffix());
+  select({"mfloat-abi=softfp"}, MS, MFM, Selected);
+  EXPECT_EQ("/f", Selected.gccSuffix());
+  select({"mfloat-abi=hard"}, MS, MFM, 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;
+  MultilibFlagMap MFM;
+  Multilib Selected;
+  ASSERT_TRUE(parse(MS, MFM, 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]
+)"));
+  select({"mfloat-abi=soft"}, MS, MFM, Selected);
+  EXPECT_EQ("/s", Selected.gccSuffix());
+  select({"mfloat-abi=softfp"}, MS, MFM, Selected);
+  EXPECT_EQ("/s", Selected.gccSuffix());
+  select({"mfloat-abi=hard"}, MS, MFM, Selected);
+  EXPECT_EQ("/h", Selected.gccSuffix());
+}
+
+TEST(MultilibTest, SelectMClass) {
+  const char *MultilibSpec = 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;
+  MultilibFlagMap MFM;
+  Multilib Selected;
+  ASSERT_TRUE(parse(MS, MFM, MultilibSpec));
+
+  EXPECT_TRUE(select({"target=thumbv6m-none-eabi", "mfloat-abi=soft"}, MS, MFM,
+                     Selected));
+  EXPECT_EQ("/thumb/v6-m/nofp", Selected.gccSuffix());
+
+  EXPECT_TRUE(select({"target=thumbv7m-none-eabi", "mfloat-abi=soft"}, MS, MFM,
+                     Selected));
+  EXPECT_EQ("/thumb/v7-m/nofp", Selected.gccSuffix());
+
+  EXPECT_TRUE(
+      select({"target=thumbv7em-none-eabi", "mfloat-abi=soft", "mfpu=none"}, MS,
+             MFM, Selected));
+  EXPECT_EQ("/thumb/v7e-m/nofp", Selected.gccSuffix());
+
+  EXPECT_TRUE(select({"target=thumbv8m.main-none-eabi", "mfloat-abi=soft"}, MS,
+                     MFM, Selected));
+  EXPECT_EQ("/thumb/v8-m.main/nofp", Selected.gccSuffix());
+
+  EXPECT_TRUE(select(
+      {"target=thumbv8.1m.main-none-eabi", "mfloat-abi=soft", "mfpu=none"}, MS,
+      MFM, Selected));
+  EXPECT_EQ("/thumb/v8.1-m.main/nofp/nomve", Selected.gccSuffix());
+
+  EXPECT_TRUE(select(
+      {"target=thumbv7em-none-eabihf", "mfloat-abi=hard", "mfpu=fpv4-sp-d16"},
+      MS, MFM, Selected));
+  EXPECT_EQ("/thumb/v7e-m/fpv4_sp_d16", Selected.gccSuffix());
+
+  EXPECT_TRUE(select(
+      {"target=thumbv7em-none-eabihf", "mfloat-abi=hard", "mfpu=fpv5-d16"}, MS,
+      MFM, Selected));
+  EXPECT_EQ("/thumb/v7e-m/fpv5_d16", Selected.gccSuffix());
+
+  EXPECT_TRUE(select({"target=thumbv8m.main-none-eabihf", "mfloat-abi=hard"},
+                     MS, MFM, Selected));
+  EXPECT_EQ("/thumb/v8-m.main/fp", Selected.gccSuffix());
+
+  EXPECT_TRUE(select({"target=thumbv8.1m.main-none-eabihf", "mfloat-abi=hard"},
+                     MS, MFM, Selected));
+  EXPECT_EQ("/thumb/v8.1-m.main/fp", Selected.gccSuffix());
+
+  EXPECT_TRUE(select({"target=thumbv8.1m.main-none-eabihf", "mfloat-abi=hard",
+                      "mfpu=none", "march=+mve"},
+                     MS, MFM, 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
@@ -15,11 +15,11 @@
 #include "llvm/Support/Compiler.h"
 #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>
+#include <regex>
 
 using namespace clang;
 using namespace driver;
@@ -75,6 +75,23 @@
   return OS;
 }
 
+Multilib::flags_list
+MultilibFlagMap::getFlags(ArrayRef<std::string> InFlags) const {
+  std::set<std::string> Result;
+  for (const Matcher &M : Matchers) {
+    const std::regex Regex(M.Regex);
+    if (std::find_if(InFlags.begin(), InFlags.end(),
+                     [&Regex](const std::string &F) {
+                       return std::regex_match(F, Regex);
+                     }) != InFlags.end()) {
+      Result.insert(M.MatchFlags.begin(), M.MatchFlags.end());
+    } else {
+      Result.insert(M.NoMatchFlags.begin(), M.NoMatchFlags.end());
+    }
+  }
+  return Result;
+}
+
 MultilibSet &MultilibSet::FilterOut(FilterCallback F) {
   llvm::erase_if(Multilibs, F);
   return *this;
@@ -103,6 +120,76 @@
   return true;
 }
 
+struct MultilibSerialization {
+  std::string Dir;
+  std::vector<std::string> Flags, PrintArgs;
+};
+
+struct MultilibSetSerialization {
+  std::vector<MultilibSerialization> Multilibs;
+  std::vector<MultilibFlagMap::Matcher> Matchers;
+};
+
+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<MultilibFlagMap::Matcher> {
+  static void mapping(llvm::yaml::IO &io, MultilibFlagMap::Matcher &M) {
+    io.mapRequired("regex", M.Regex);
+    io.mapOptional("matchFlags", M.MatchFlags);
+    io.mapOptional("noMatchFlags", M.NoMatchFlags);
+  }
+  static std::string validate(IO &io, MultilibFlagMap::Matcher &M) {
+    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.mapOptional("variants", M.Multilibs);
+    io.mapOptional("flagMap", M.Matchers);
+  }
+};
+
+LLVM_YAML_IS_SEQUENCE_VECTOR(MultilibSerialization)
+LLVM_YAML_IS_SEQUENCE_VECTOR(MultilibFlagMap::Matcher)
+
+bool MultilibSet::parse(llvm::MemoryBufferRef Input, MultilibFlagMap &MFM,
+                        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::flags_list(M.Flags.begin(), M.Flags.end()),
+                           M.PrintArgs);
+  }
+  MFM = MultilibFlagMap(std::move(MS.Matchers));
+  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>
@@ -74,6 +75,29 @@
 
 raw_ostream &operator<<(raw_ostream &OS, const Multilib &M);
 
+/// 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.
+class MultilibFlagMap {
+public:
+  struct Matcher {
+    std::string Regex;
+    std::vector<std::string> MatchFlags, NoMatchFlags;
+  };
+
+  MultilibFlagMap() = default;
+  MultilibFlagMap(MultilibFlagMap &&) = default;
+  MultilibFlagMap(std::vector<Matcher> &&Matchers) : Matchers(Matchers) {}
+  MultilibFlagMap &operator=(MultilibFlagMap &&) = default;
+
+  Multilib::flags_list getFlags(ArrayRef<std::string> InFlags) const;
+
+private:
+  std::vector<Matcher> Matchers;
+};
+
 class MultilibSet {
 public:
   using multilib_list = std::vector<Multilib>;
@@ -110,6 +134,10 @@
 
   unsigned size() const { return Multilibs.size(); }
 
+  bool parse(llvm::MemoryBufferRef, MultilibFlagMap &,
+             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