r201202 - Add Multilib selection machinery

Jonathan Roelofs jonathan at codesourcery.com
Tue Feb 11 17:29:25 PST 2014


Author: jroelofs
Date: Tue Feb 11 19:29:25 2014
New Revision: 201202

URL: http://llvm.org/viewvc/llvm-project?rev=201202&view=rev
Log:
Add Multilib selection machinery

This patch improves the support for picking Multilibs from gcc installations.
It also provides a better approximation for the flags '-print-multi-directory'
and '-print-multi-lib'.

review: http://llvm-reviews.chandlerc.com/D2538

Added:
    cfe/trunk/include/clang/Driver/Multilib.h
    cfe/trunk/lib/Driver/Multilib.cpp
    cfe/trunk/unittests/Driver/
    cfe/trunk/unittests/Driver/CMakeLists.txt
    cfe/trunk/unittests/Driver/Makefile
    cfe/trunk/unittests/Driver/MultilibTest.cpp
Modified:
    cfe/trunk/include/clang/Driver/ToolChain.h
    cfe/trunk/lib/Driver/CMakeLists.txt
    cfe/trunk/lib/Driver/Driver.cpp
    cfe/trunk/lib/Driver/ToolChains.cpp
    cfe/trunk/lib/Driver/ToolChains.h
    cfe/trunk/lib/Driver/Tools.cpp
    cfe/trunk/lib/Driver/Tools.h
    cfe/trunk/unittests/CMakeLists.txt
    cfe/trunk/unittests/Makefile

Added: cfe/trunk/include/clang/Driver/Multilib.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Driver/Multilib.h?rev=201202&view=auto
==============================================================================
--- cfe/trunk/include/clang/Driver/Multilib.h (added)
+++ cfe/trunk/include/clang/Driver/Multilib.h Tue Feb 11 19:29:25 2014
@@ -0,0 +1,163 @@
+//===--- Multilib.h ---------------------------------------------*- C++ -*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef CLANG_LIB_DRIVER_MULTILIB_H_
+#define CLANG_LIB_DRIVER_MULTILIB_H_
+
+#include "clang/Basic/LLVM.h"
+#include "llvm/ADT/Triple.h"
+#include "llvm/Option/Option.h"
+#include <string>
+#include <vector>
+
+namespace clang {
+namespace driver {
+
+/// This corresponds to a single GCC Multilib, or a segment of one controlled
+/// by a command line flag
+class Multilib {
+public:
+  typedef std::vector<std::string> flags_list;
+
+private:
+  std::string GCCSuffix;
+  std::string OSSuffix;
+  std::string IncludeSuffix;
+  flags_list Flags;
+
+public:
+  Multilib(StringRef GCCSuffix = "", StringRef OSSuffix = "",
+           StringRef IncludeSuffix = "");
+
+  /// \brief Get the detected GCC installation path suffix for the multi-arch
+  /// target variant. Always starts with a '/', unless empty
+  const std::string &gccSuffix() const {
+    assert(GCCSuffix.empty() ||
+           (StringRef(GCCSuffix).front() == '/' && GCCSuffix.size() > 1));
+    return GCCSuffix;
+  }
+  /// Set the GCC installation path suffix.
+  Multilib &gccSuffix(StringRef S);
+
+  /// \brief Get the detected os path suffix for the multi-arch
+  /// target variant. Always starts with a '/', unless empty
+  const std::string &osSuffix() const {
+    assert(OSSuffix.empty() ||
+           (StringRef(OSSuffix).front() == '/' && OSSuffix.size() > 1));
+    return OSSuffix;
+  }
+  /// Set the os path suffix.
+  Multilib &osSuffix(StringRef S);
+
+  /// \brief Get the include directory suffix. Always starts with a '/', unless
+  /// empty
+  const std::string &includeSuffix() const {
+    assert(IncludeSuffix.empty() ||
+           (StringRef(IncludeSuffix).front() == '/' && IncludeSuffix.size() > 1));
+    return IncludeSuffix;
+  }
+  /// Set the include directory suffix
+  Multilib &includeSuffix(StringRef S);
+
+  /// \brief Get the flags that indicate or contraindicate this multilib's use
+  /// All elements begin with either '+' or '-'
+  const flags_list &flags() const { return Flags; }
+  flags_list &flags() { return Flags; }
+  /// Add a flag to the flags list
+  Multilib &flag(StringRef F) {
+    assert(F.front() == '+' || F.front() == '-');
+    Flags.push_back(F);
+    return *this;
+  }
+
+  /// \brief print summary of the Multilib
+  void print(raw_ostream &OS) const;
+
+  /// Check whether any of the 'against' flags contradict the 'for' flags.
+  bool isValid() const;
+
+  bool operator==(const Multilib &Other) const;
+};
+
+raw_ostream &operator<<(raw_ostream &OS, const Multilib &M);
+
+class MultilibSet {
+public:
+  typedef std::vector<Multilib> multilib_list;
+  typedef multilib_list::iterator iterator;
+  typedef multilib_list::const_iterator const_iterator;
+
+  struct FilterCallback {
+    virtual ~FilterCallback() {};
+    /// \return true iff the filter should remove the Multilib from the set
+    virtual bool operator()(const Multilib &M) const = 0;
+  };
+
+private:
+  multilib_list Multilibs;
+
+public:
+  MultilibSet() {}
+
+  /// Add an optional Multilib segment
+  MultilibSet &Maybe(const Multilib &M);
+
+  /// Add a set of mutually incompatible Multilib segments
+  MultilibSet &Either(const Multilib &M1, const Multilib &M2);
+  MultilibSet &Either(const Multilib &M1, const Multilib &M2,
+                      const Multilib &M3);
+  MultilibSet &Either(const Multilib &M1, const Multilib &M2,
+                      const Multilib &M3, const Multilib &M4);
+  MultilibSet &Either(const Multilib &M1, const Multilib &M2,
+                      const Multilib &M3, const Multilib &M4,
+                      const Multilib &M5);
+  MultilibSet &Either(const std::vector<Multilib> &Ms);
+
+  /// Filter out some subset of the Multilibs using a user defined callback
+  MultilibSet &FilterOut(const FilterCallback &F);
+  /// Filter out those Multilibs whose gccSuffix matches the given expression
+  MultilibSet &FilterOut(std::string Regex);
+
+  /// Add a completed Multilib to the set
+  void push_back(const Multilib &M);
+
+  /// Union this set of multilibs with another
+  void combineWith(const MultilibSet &MS);
+
+  /// Remove all of thie multilibs from the set
+  void clear() { Multilibs.clear(); }
+
+  iterator begin() { return Multilibs.begin(); }
+  const_iterator begin() const { return Multilibs.begin(); }
+
+  iterator end() { return Multilibs.end(); }
+  const_iterator end() const { return Multilibs.end(); }
+
+  /// Pick the best multilib in the set, \returns false if none are compatible
+  bool select(const Multilib::flags_list &Flags, Multilib &M) const;
+
+  unsigned size() const { return Multilibs.size(); }
+
+  void print(raw_ostream &OS) const;
+
+private:
+  /// Apply the filter to Multilibs and return the subset that remains
+  static multilib_list filterCopy(const FilterCallback &F,
+                                  const multilib_list &Ms);
+
+  /// Apply the filter to the multilib_list, removing those that don't match
+  static void filterInPlace(const FilterCallback &F, multilib_list &Ms);
+};
+
+raw_ostream &operator<<(raw_ostream &OS, const MultilibSet &MS);
+}
+}
+
+#endif
+

Modified: cfe/trunk/include/clang/Driver/ToolChain.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Driver/ToolChain.h?rev=201202&r1=201201&r2=201202&view=diff
==============================================================================
--- cfe/trunk/include/clang/Driver/ToolChain.h (original)
+++ cfe/trunk/include/clang/Driver/ToolChain.h Tue Feb 11 19:29:25 2014
@@ -11,6 +11,7 @@
 #define CLANG_DRIVER_TOOLCHAIN_H_
 
 #include "clang/Driver/Action.h"
+#include "clang/Driver/Multilib.h"
 #include "clang/Driver/Types.h"
 #include "clang/Driver/Util.h"
 #include "llvm/ADT/OwningPtr.h"
@@ -76,6 +77,8 @@ private:
   mutable OwningPtr<SanitizerArgs> SanitizerArguments;
 
 protected:
+  MultilibSet Multilibs;
+
   ToolChain(const Driver &D, const llvm::Triple &T,
             const llvm::opt::ArgList &Args);
 
@@ -130,6 +133,8 @@ public:
   path_list &getProgramPaths() { return ProgramPaths; }
   const path_list &getProgramPaths() const { return ProgramPaths; }
 
+  const MultilibSet &getMultilibs() const { return Multilibs; }
+
   const SanitizerArgs& getSanitizerArgs() const;
 
   // Tool access.

Modified: cfe/trunk/lib/Driver/CMakeLists.txt
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Driver/CMakeLists.txt?rev=201202&r1=201201&r2=201202&view=diff
==============================================================================
--- cfe/trunk/lib/Driver/CMakeLists.txt (original)
+++ cfe/trunk/lib/Driver/CMakeLists.txt Tue Feb 11 19:29:25 2014
@@ -11,6 +11,7 @@ add_clang_library(clangDriver
   Driver.cpp
   DriverOptions.cpp
   Job.cpp
+  Multilib.cpp
   Phases.cpp
   SanitizerArgs.cpp
   Tool.cpp

Modified: cfe/trunk/lib/Driver/Driver.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Driver/Driver.cpp?rev=201202&r1=201201&r2=201202&view=diff
==============================================================================
--- cfe/trunk/lib/Driver/Driver.cpp (original)
+++ cfe/trunk/lib/Driver/Driver.cpp Tue Feb 11 19:29:25 2014
@@ -748,54 +748,37 @@ bool Driver::HandleImmediateArgs(const C
   }
 
   if (C.getArgs().hasArg(options::OPT_print_multi_lib)) {
-    // FIXME: We need tool chain support for this.
-    llvm::outs() << ".;\n";
+    const MultilibSet &Multilibs = TC.getMultilibs();
 
-    switch (C.getDefaultToolChain().getTriple().getArch()) {
-    default:
-      break;
-
-    case llvm::Triple::x86_64:
-      llvm::outs() << "x86_64;@m64" << "\n";
-      break;
-
-    case llvm::Triple::ppc64:
-      llvm::outs() << "ppc64;@m64" << "\n";
-      break;
-
-    case llvm::Triple::ppc64le:
-      llvm::outs() << "ppc64le;@m64" << "\n";
-      break;
+    for (MultilibSet::const_iterator I = Multilibs.begin(), E = Multilibs.end();
+         I != E; ++I) {
+      llvm::outs() << *I << "\n";
     }
     return false;
   }
 
-  // FIXME: What is the difference between print-multi-directory and
-  // print-multi-os-directory?
-  if (C.getArgs().hasArg(options::OPT_print_multi_directory) ||
-      C.getArgs().hasArg(options::OPT_print_multi_os_directory)) {
-    switch (C.getDefaultToolChain().getTriple().getArch()) {
-    default:
-    case llvm::Triple::x86:
-    case llvm::Triple::ppc:
-      llvm::outs() << "." << "\n";
-      break;
-
-    case llvm::Triple::x86_64:
-      llvm::outs() << "x86_64" << "\n";
-      break;
-
-    case llvm::Triple::ppc64:
-      llvm::outs() << "ppc64" << "\n";
-      break;
-
-    case llvm::Triple::ppc64le:
-      llvm::outs() << "ppc64le" << "\n";
-      break;
+  if (C.getArgs().hasArg(options::OPT_print_multi_directory)) {
+    const MultilibSet &Multilibs = TC.getMultilibs();
+    for (MultilibSet::const_iterator I = Multilibs.begin(), E = Multilibs.end();
+         I != E; ++I) {
+      if (I->gccSuffix().empty())
+        llvm::outs() << ".\n";
+      else {
+        StringRef Suffix(I->gccSuffix());
+        assert(Suffix.front() == '/');
+        llvm::outs() << Suffix.substr(1) << "\n";
+      }
     }
     return false;
   }
 
+  if (C.getArgs().hasArg(options::OPT_print_multi_os_directory)) {
+    // FIXME: This should print out "lib/../lib", "lib/../lib64", or
+    // "lib/../lib32" as appropriate for the toolchain. For now, print
+    // nothing because it's not supported yet.
+    return false;
+  }
+
   return true;
 }
 

Added: cfe/trunk/lib/Driver/Multilib.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Driver/Multilib.cpp?rev=201202&view=auto
==============================================================================
--- cfe/trunk/lib/Driver/Multilib.cpp (added)
+++ cfe/trunk/lib/Driver/Multilib.cpp Tue Feb 11 19:29:25 2014
@@ -0,0 +1,347 @@
+//===--- Multilib.cpp - Multilib Implementation ---------------------------===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang/Driver/Multilib.h"
+#include "Tools.h"
+#include "clang/Driver/Options.h"
+#include "llvm/ADT/StringMap.h"
+#include "llvm/ADT/StringRef.h"
+#include "llvm/ADT/StringSet.h"
+#include "llvm/ADT/Triple.h"
+#include "llvm/Option/Arg.h"
+#include "llvm/Option/ArgList.h"
+#include "llvm/Option/OptTable.h"
+#include "llvm/Option/Option.h"
+#include "llvm/Support/MemoryBuffer.h"
+#include "llvm/Support/Path.h"
+#include "llvm/Support/raw_ostream.h"
+#include "llvm/Support/Regex.h"
+#include "llvm/Support/YAMLParser.h"
+#include "llvm/Support/YAMLTraits.h"
+#include <algorithm>
+
+using namespace clang::driver;
+using namespace clang;
+using namespace llvm::opt;
+
+static void normalizePathSegment(std::string &Segment) {
+  StringRef SRS(Segment);
+  if (SRS.empty() || SRS == "/." || SRS == "/" || SRS == ".") {
+    SRS = "";
+  } else {
+    if (SRS.back() == '/')
+      SRS = SRS.drop_back();
+    if (SRS.front() != '/')
+      SRS = ("/" + SRS).str();
+  }
+  Segment = SRS;
+}
+
+Multilib::Multilib(StringRef GCCSuffix, StringRef OSSuffix,
+                   StringRef IncludeSuffix)
+    : GCCSuffix(GCCSuffix), OSSuffix(OSSuffix), IncludeSuffix(IncludeSuffix) {
+  normalizePathSegment(this->GCCSuffix);
+  normalizePathSegment(this->OSSuffix);
+  normalizePathSegment(this->IncludeSuffix);
+}
+
+Multilib &Multilib::gccSuffix(StringRef S) {
+  GCCSuffix = S;
+  normalizePathSegment(GCCSuffix);
+  return *this;
+}
+
+Multilib &Multilib::osSuffix(StringRef S) {
+  OSSuffix = S;
+  normalizePathSegment(OSSuffix);
+  return *this;
+}
+
+Multilib &Multilib::includeSuffix(StringRef S) {
+  IncludeSuffix = S;
+  normalizePathSegment(IncludeSuffix);
+  return *this;
+}
+
+void Multilib::print(raw_ostream &OS) const {
+  assert(GCCSuffix.empty() || (StringRef(GCCSuffix).front() == '/'));
+  if (GCCSuffix.empty())
+    OS << ".";
+  else {
+    OS << StringRef(GCCSuffix).drop_front();
+  }
+  OS << ";";
+  for (flags_list::const_iterator I = Flags.begin(), E = Flags.end(); I != E;
+       ++I) {
+    if (StringRef(*I).front() == '+')
+      OS << "@" << I->substr(1);
+  }
+}
+
+bool Multilib::isValid() const {
+  llvm::StringMap<int> FlagSet;
+  for (unsigned I = 0, N = Flags.size(); I != N; ++I) {
+    StringRef Flag(Flags[I]);
+    llvm::StringMap<int>::iterator SI = FlagSet.find(Flag.substr(1));
+
+    assert(StringRef(Flag).front() == '+' || StringRef(Flag).front() == '-');
+
+    if (SI == FlagSet.end())
+      FlagSet[Flag.substr(1)] = I;
+    else if (Flags[I] != Flags[SI->getValue()])
+      return false;
+  }
+  return true;
+}
+
+bool Multilib::operator==(const Multilib &Other) const {
+  // Check whether the flags sets match
+  // allowing for the match to be order invariant
+  llvm::StringSet<> MyFlags;
+  for (flags_list::const_iterator I = Flags.begin(), E = Flags.end(); I != E;
+       ++I) {
+    MyFlags.insert(*I);
+  }
+  for (flags_list::const_iterator I = Other.Flags.begin(),
+                                  E = Other.Flags.end();
+       I != E; ++I) {
+    if (MyFlags.find(*I) == MyFlags.end())
+      return false;
+  }
+
+  if (osSuffix() != Other.osSuffix())
+    return false;
+
+  if (gccSuffix() != Other.gccSuffix())
+    return false;
+
+  if (includeSuffix() != Other.includeSuffix())
+    return false;
+
+  return true;
+}
+
+raw_ostream &clang::driver::operator<<(raw_ostream &OS, const Multilib &M) {
+  M.print(OS);
+  return OS;
+}
+
+MultilibSet &MultilibSet::Maybe(const Multilib &M) {
+  Multilib Opposite;
+  // Negate any '+' flags
+  for (Multilib::flags_list::const_iterator I = M.flags().begin(),
+                                            E = M.flags().end();
+       I != E; ++I) {
+    StringRef Flag(*I);
+    if (Flag.front() == '+')
+      Opposite.flags().push_back(("-" + Flag.substr(1)).str());
+  }
+  return Either(M, Opposite);
+}
+
+MultilibSet &MultilibSet::Either(const Multilib &M1, const Multilib &M2) {
+  std::vector<Multilib> Ms;
+  Ms.push_back(M1);
+  Ms.push_back(M2);
+  return Either(Ms);
+}
+
+MultilibSet &MultilibSet::Either(const Multilib &M1, const Multilib &M2,
+                                 const Multilib &M3) {
+  std::vector<Multilib> Ms;
+  Ms.push_back(M1);
+  Ms.push_back(M2);
+  Ms.push_back(M3);
+  return Either(Ms);
+}
+
+MultilibSet &MultilibSet::Either(const Multilib &M1, const Multilib &M2,
+                                 const Multilib &M3, const Multilib &M4) {
+  std::vector<Multilib> Ms;
+  Ms.push_back(M1);
+  Ms.push_back(M2);
+  Ms.push_back(M3);
+  Ms.push_back(M4);
+  return Either(Ms);
+}
+
+MultilibSet &MultilibSet::Either(const Multilib &M1, const Multilib &M2,
+                                 const Multilib &M3, const Multilib &M4,
+                                 const Multilib &M5) {
+  std::vector<Multilib> Ms;
+  Ms.push_back(M1);
+  Ms.push_back(M2);
+  Ms.push_back(M3);
+  Ms.push_back(M4);
+  Ms.push_back(M5);
+  return Either(Ms);
+}
+
+static Multilib compose(const Multilib &Base, const Multilib &New) {
+  SmallString<128> GCCSuffix;
+  llvm::sys::path::append(GCCSuffix, "/", Base.gccSuffix(), New.gccSuffix());
+  SmallString<128> OSSuffix;
+  llvm::sys::path::append(OSSuffix, "/", Base.osSuffix(), New.osSuffix());
+  SmallString<128> IncludeSuffix;
+  llvm::sys::path::append(IncludeSuffix, "/", Base.includeSuffix(),
+                          New.includeSuffix());
+
+  Multilib Composed(GCCSuffix.str(), OSSuffix.str(), IncludeSuffix.str());
+
+  Multilib::flags_list &Flags = Composed.flags();
+
+  Flags.insert(Flags.end(), Base.flags().begin(), Base.flags().end());
+  Flags.insert(Flags.end(), New.flags().begin(), New.flags().end());
+
+  return Composed;
+}
+
+MultilibSet &
+MultilibSet::Either(const std::vector<Multilib> &MultilibSegments) {
+  multilib_list Composed;
+
+  if (Multilibs.empty())
+    Multilibs.insert(Multilibs.end(), MultilibSegments.begin(),
+                     MultilibSegments.end());
+  else {
+    for (std::vector<Multilib>::const_iterator NewI = MultilibSegments.begin(),
+                                               NewE = MultilibSegments.end();
+         NewI != NewE; ++NewI) {
+      for (const_iterator BaseI = begin(), BaseE = end(); BaseI != BaseE;
+           ++BaseI) {
+        Multilib MO = compose(*BaseI, *NewI);
+        if (MO.isValid())
+          Composed.push_back(MO);
+      }
+    }
+
+    Multilibs = Composed;
+  }
+
+  return *this;
+}
+
+MultilibSet &MultilibSet::FilterOut(const MultilibSet::FilterCallback &F) {
+  filterInPlace(F, Multilibs);
+  return *this;
+}
+
+MultilibSet &MultilibSet::FilterOut(std::string Regex) {
+  class REFilter : public MultilibSet::FilterCallback {
+    mutable llvm::Regex R;
+
+  public:
+    REFilter(std::string Regex) : R(Regex) {}
+    bool operator()(const Multilib &M) const LLVM_OVERRIDE {
+      std::string Error;
+      if (!R.isValid(Error)) {
+        llvm::errs() << Error;
+        assert(false);
+        return false;
+      }
+      return R.match(M.gccSuffix());
+    }
+  };
+
+  REFilter REF(Regex);
+  filterInPlace(REF, Multilibs);
+  return *this;
+}
+
+void MultilibSet::push_back(const Multilib &M) { Multilibs.push_back(M); }
+
+void MultilibSet::combineWith(const MultilibSet &Other) {
+  Multilibs.insert(Multilibs.end(), Other.begin(), Other.end());
+}
+
+bool MultilibSet::select(const Multilib::flags_list &Flags, Multilib &M) const {
+  class FilterFlagsMismatch : public MultilibSet::FilterCallback {
+    llvm::StringMap<bool> FlagSet;
+
+  public:
+    FilterFlagsMismatch(const std::vector<std::string> &Flags) {
+      // Stuff all of the flags into the FlagSet such that a true mappend
+      // indicates the flag was enabled, and a false mappend indicates the
+      // flag was disabled
+      for (Multilib::flags_list::const_iterator I = Flags.begin(),
+                                                E = Flags.end();
+           I != E; ++I) {
+        FlagSet[StringRef(*I).substr(1)] = isFlagEnabled(*I);
+      }
+    }
+    bool operator()(const Multilib &M) const LLVM_OVERRIDE {
+      for (Multilib::flags_list::const_iterator I = M.flags().begin(),
+                                                E = M.flags().end();
+           I != E; ++I) {
+        StringRef Flag(*I);
+        llvm::StringMap<bool>::const_iterator SI = FlagSet.find(Flag.substr(1));
+        if (SI != FlagSet.end())
+          if ((*SI).getValue() != isFlagEnabled(Flag))
+            return true;
+      }
+      return false;
+    }
+  private:
+    bool isFlagEnabled(StringRef Flag) const {
+      char Indicator = Flag.front();
+      assert(Indicator == '+' || Indicator == '-');
+      return Indicator == '+';
+    }
+  };
+
+  FilterFlagsMismatch FlagsMismatch(Flags);
+
+  multilib_list Filtered = filterCopy(FlagsMismatch, Multilibs);
+
+  if (Filtered.size() == 0) {
+    return false;
+  } else if (Filtered.size() == 1) {
+    M = Filtered[0];
+    return true;
+  }
+
+  // TODO: pick the "best" multlib when more than one is suitable
+  assert(false);
+
+  return false;
+}
+
+void MultilibSet::print(raw_ostream &OS) const {
+  for (const_iterator I = begin(), E = end(); I != E; ++I)
+    OS << *I << "\n";
+}
+
+MultilibSet::multilib_list
+MultilibSet::filterCopy(const MultilibSet::FilterCallback &F,
+                        const multilib_list &Ms) {
+  multilib_list Copy(Ms);
+  filterInPlace(F, Copy);
+  return Copy;
+}
+
+namespace {
+// Wrapper for FilterCallback to make operator() nonvirtual so it
+// can be passed by value to std::remove_if
+class FilterWrapper {
+  const MultilibSet::FilterCallback &F;
+public:
+  FilterWrapper(const MultilibSet::FilterCallback &F) : F(F) {}
+  bool operator()(const Multilib &M) const { return F(M); }
+};
+} // end anonymous namespace
+
+void MultilibSet::filterInPlace(const MultilibSet::FilterCallback &F,
+                                multilib_list &Ms) {
+  Ms.erase(std::remove_if(Ms.begin(), Ms.end(), FilterWrapper(F)), Ms.end());
+}
+
+raw_ostream &clang::driver::operator<<(raw_ostream &OS, const MultilibSet &MS) {
+  MS.print(OS);
+  return OS;
+}

Modified: cfe/trunk/lib/Driver/ToolChains.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Driver/ToolChains.cpp?rev=201202&r1=201201&r2=201202&view=diff
==============================================================================
--- cfe/trunk/lib/Driver/ToolChains.cpp (original)
+++ cfe/trunk/lib/Driver/ToolChains.cpp Tue Feb 11 19:29:25 2014
@@ -139,11 +139,11 @@ static const char *GetArmArchForMCpu(Str
 }
 
 static bool isSoftFloatABI(const ArgList &Args) {
-  Arg *A = Args.getLastArg(options::OPT_msoft_float,
-                           options::OPT_mhard_float,
+  Arg *A = Args.getLastArg(options::OPT_msoft_float, options::OPT_mhard_float,
                            options::OPT_mfloat_abi_EQ);
-  if (!A) return false;
-
+  if (!A)
+    return false;
+ 
   return A->getOption().matches(options::OPT_msoft_float) ||
          (A->getOption().matches(options::OPT_mfloat_abi_EQ) &&
           A->getValue() == StringRef("soft"));
@@ -1175,7 +1175,7 @@ bool Generic_GCC::GCCVersion::isOlderTha
   return false;
 }
 
-static StringRef getGCCToolchainDir(const ArgList &Args) {
+static llvm::StringRef getGCCToolchainDir(const ArgList &Args) {
   const Arg *A = Args.getLastArg(options::OPT_gcc_toolchain);
   if (A)
     return A->getValue();
@@ -1197,7 +1197,6 @@ Generic_GCC::GCCInstallationDetector::in
   llvm::Triple BiarchVariantTriple =
       TargetTriple.isArch32Bit() ? TargetTriple.get64BitArchVariant()
                                  : TargetTriple.get32BitArchVariant();
-  llvm::Triple::ArchType TargetArch = TargetTriple.getArch();
   // The library directories which may contain GCC installations.
   SmallVector<StringRef, 4> CandidateLibDirs, CandidateBiarchLibDirs;
   // The compatible GCC triples for this particular architecture.
@@ -1243,7 +1242,7 @@ Generic_GCC::GCCInstallationDetector::in
       if (!llvm::sys::fs::exists(LibDir))
         continue;
       for (unsigned k = 0, ke = CandidateTripleAliases.size(); k < ke; ++k)
-        ScanLibDirForGCCTriple(TargetArch, Args, LibDir,
+        ScanLibDirForGCCTriple(TargetTriple, Args, LibDir,
                                CandidateTripleAliases[k]);
     }
     for (unsigned j = 0, je = CandidateBiarchLibDirs.size(); j < je; ++j) {
@@ -1252,7 +1251,7 @@ Generic_GCC::GCCInstallationDetector::in
         continue;
       for (unsigned k = 0, ke = CandidateBiarchTripleAliases.size(); k < ke;
            ++k)
-        ScanLibDirForGCCTriple(TargetArch, Args, LibDir,
+        ScanLibDirForGCCTriple(TargetTriple, Args, LibDir,
                                CandidateBiarchTripleAliases[k],
                                /*NeedsBiarchSuffix=*/ true);
     }
@@ -1267,6 +1266,19 @@ void Generic_GCC::GCCInstallationDetecto
     OS << "Found candidate GCC installation: " << *I << "\n";
 
   OS << "Selected GCC installation: " << GCCInstallPath << "\n";
+  for (MultilibSet::const_iterator I = Multilibs.begin(), E = Multilibs.end();
+       I != E; ++I) {
+    OS << "Candidate multiilb: " << *I << "\n";
+  }
+  OS << "Selected multilib: " << SelectedMultilib << "\n";
+}
+
+bool Generic_GCC::GCCInstallationDetector::getBiarchSibling(Multilib &M) const {
+  if (BiarchSibling.hasValue()) {
+    M = BiarchSibling.getValue();
+    return true;
+  }
+  return false;
 }
 
 /*static*/ void Generic_GCC::GCCInstallationDetector::CollectLibDirsAndTriples(
@@ -1502,11 +1514,45 @@ void Generic_GCC::GCCInstallationDetecto
     BiarchTripleAliases.push_back(BiarchTriple.str());
 }
 
+namespace {
+// Filter to remove Multilibs that don't exist as a suffix to Path
+class FilterNonExistant : public MultilibSet::FilterCallback {
+  std::string Base;
+public:
+  FilterNonExistant(std::string Base) : Base(Base) {}
+  bool operator()(const Multilib &M) const LLVM_OVERRIDE {
+    return !llvm::sys::fs::exists(Base + M.gccSuffix() + "/crtbegin.o");
+  }
+};
+} // end anonymous namespace
+
+static void addMultilibFlag(bool Enabled, const char *const Flag,
+                            std::vector<std::string> &Flags) {
+  if (Enabled)
+    Flags.push_back(std::string("+") + Flag);
+  else
+    Flags.push_back(std::string("-") + Flag);
+}
+
 static bool isMipsArch(llvm::Triple::ArchType Arch) {
-  return Arch == llvm::Triple::mips ||
-         Arch == llvm::Triple::mipsel ||
-         Arch == llvm::Triple::mips64 ||
-         Arch == llvm::Triple::mips64el;
+  return Arch == llvm::Triple::mips || Arch == llvm::Triple::mipsel ||
+         Arch == llvm::Triple::mips64 || Arch == llvm::Triple::mips64el;
+}
+
+static bool isMips32(llvm::Triple::ArchType Arch) {
+  return Arch == llvm::Triple::mips || Arch == llvm::Triple::mipsel;
+}
+
+static bool isMips64(llvm::Triple::ArchType Arch) {
+  return Arch == llvm::Triple::mips64 || Arch == llvm::Triple::mips64el;
+}
+
+static bool isMipsEL(llvm::Triple::ArchType Arch) {
+  return Arch == llvm::Triple::mipsel || Arch == llvm::Triple::mips64el;
+}
+
+static bool isMipsEB(llvm::Triple::ArchType Arch) {
+  return Arch == llvm::Triple::mips || Arch == llvm::Triple::mips64;
 }
 
 static bool isMips16(const ArgList &Args) {
@@ -1545,39 +1591,8 @@ static bool isMipsNan2008(const ArgList
   return A && A->getValue() == StringRef("2008");
 }
 
-// FIXME: There is the same routine in the Tools.cpp.
-static bool hasMipsABIArg(const ArgList &Args, const char *Value) {
-  Arg *A = Args.getLastArg(options::OPT_mabi_EQ);
-  return A && (A->getValue() == StringRef(Value));
-}
-
-static bool hasCrtBeginObj(Twine Path) {
-  return llvm::sys::fs::exists(Path + "/crtbegin.o");
-}
-
-static bool findTargetBiarchSuffix(std::string &Suffix, StringRef Path,
-                                   llvm::Triple::ArchType TargetArch,
-                                   const ArgList &Args) {
-  // FIXME: This routine was only intended to model bi-arch toolchains which
-  // use -m32 and -m64 to swap between variants of a target. It shouldn't be
-  // doing ABI-based builtin location for MIPS.
-  if (hasMipsABIArg(Args, "n32"))
-    Suffix = "/n32";
-  else if (TargetArch == llvm::Triple::x86_64 ||
-           TargetArch == llvm::Triple::ppc64 ||
-           TargetArch == llvm::Triple::sparcv9 ||
-           TargetArch == llvm::Triple::systemz ||
-           TargetArch == llvm::Triple::mips64 ||
-           TargetArch == llvm::Triple::mips64el)
-    Suffix = "/64";
-  else
-    Suffix = "/32";
-
-  return hasCrtBeginObj(Path + Suffix);
-}
-
-void Generic_GCC::GCCInstallationDetector::findMIPSABIDirSuffix(
-    std::string &Suffix, llvm::Triple::ArchType TargetArch, StringRef Path,
+bool Generic_GCC::GCCInstallationDetector::findMIPSMultilibs(
+    const llvm::Triple &TargetTriple, StringRef Path,
     const llvm::opt::ArgList &Args) {
   // Some MIPS toolchains put libraries and object files compiled
   // using different options in to the sub-directoris which names
@@ -1604,69 +1619,286 @@ void Generic_GCC::GCCInstallationDetecto
   //     /usr
   //       /lib  <= crt*.o files compiled with '-mips32'
 
-  // Check FSF Toolchain path
-  Suffix.clear();
-  if (TargetArch == llvm::Triple::mips ||
-      TargetArch == llvm::Triple::mipsel) {
-    if (isMicroMips(Args))
-      Suffix += "/micromips";
-    else if (isMips32r2(Args))
-      Suffix += "";
-    else
-      Suffix += "/mips32";
+  FilterNonExistant NonExistant(Path);
 
-    if (isMips16(Args))
-      Suffix += "/mips16";
-  } else {
-    if (isMips64r2(Args))
-      Suffix += hasMipsABIArg(Args, "n32") ? "/mips64r2" : "/mips64r2/64";
+  // Check for FSF toolchain multilibs
+  MultilibSet FSFMipsMultilibs;
+  {
+    Multilib MArchMips32 = Multilib()
+      .gccSuffix("/mips32")
+      .osSuffix("/mips32")
+      .includeSuffix("/mips32")
+      .flag("+m32").flag("-m64").flag("-mmicromips").flag("-march=mips32r2");
+
+    Multilib MArchMicroMips = Multilib()
+      .gccSuffix("/micromips")
+      .osSuffix("/micromips")
+      .includeSuffix("/micromips")
+      .flag("+m32").flag("-m64").flag("+mmicromips");
+
+    Multilib MArchMips64r2 = Multilib()
+      .gccSuffix("/mips64r2")
+      .osSuffix("/mips64r2")
+      .includeSuffix("/mips64r2")
+      .flag("-m32").flag("+m64").flag("+march=mips64r2");
+
+    Multilib MArchMips64 = Multilib()
+      .gccSuffix("/mips64")
+      .osSuffix("/mips64")
+      .includeSuffix("/mips64")
+      .flag("-m32").flag("+m64").flag("-march=mips64r2");
+
+    Multilib MArchDefault = Multilib()
+      .flag("+m32").flag("-m64").flag("+march=mips32r2");
+
+    Multilib Mips16 = Multilib()
+      .gccSuffix("/mips16")
+      .osSuffix("/mips16")
+      .includeSuffix("/mips16")
+      .flag("+mips16");
+
+    Multilib MAbi64 = Multilib()
+      .gccSuffix("/64")
+      .osSuffix("/64")
+      .includeSuffix("/64")
+      .flag("+mabi=64").flag("-mabi=n32").flag("-m32");
+
+    Multilib LittleEndian = Multilib()
+      .gccSuffix("/el")
+      .osSuffix("/el")
+      .includeSuffix("/el")
+      .flag("+EL").flag("-EB");
+
+    Multilib SoftFloat = Multilib()
+      .gccSuffix("/sof")
+      .osSuffix("/sof")
+      .includeSuffix("/sof")
+      .flag("+msoft-float");
+
+    Multilib FP64 = Multilib()
+      .gccSuffix("/fp64")
+      .osSuffix("/fp64")
+      .includeSuffix("/fp64")
+      .flag("+mfp64");
+
+    Multilib Nan2008 = Multilib()
+      .gccSuffix("/nan2008")
+      .osSuffix("/nan2008")
+      .includeSuffix("/nan2008")
+      .flag("+mnan=2008");
+
+    FSFMipsMultilibs = MultilibSet()
+      .Either(MArchMips32, MArchMicroMips, 
+              MArchMips64r2, MArchMips64, MArchDefault)
+      .Maybe(Mips16)
+      .FilterOut("/mips64/mips16")
+      .FilterOut("/mips64r2/mips16")
+      .FilterOut("/micromips/mips16")
+      .Maybe(MAbi64)
+      .FilterOut("/micromips/64")
+      .FilterOut("/mips32/64")
+      .FilterOut("^/64")
+      .FilterOut("/mips16/64")
+      .Maybe(LittleEndian)
+      .Maybe(SoftFloat)
+      .Maybe(FP64)
+      .Maybe(Nan2008)
+      .FilterOut(".*sof/nan2008")
+      .FilterOut(".*sof/fp64")
+      .FilterOut(NonExistant);
+  }
+
+  // Check for Code Sourcery toolchain multilibs
+  MultilibSet CSMipsMultilibs;
+  {
+    Multilib MArchMips16 = Multilib()
+      .gccSuffix("/mips16")
+      .osSuffix("/mips16")
+      .includeSuffix("/mips16")
+      .flag("+m32").flag("+mips16");
+
+    Multilib MArchMicroMips = Multilib()
+      .gccSuffix("/micromips")
+      .osSuffix("/micromips")
+      .includeSuffix("/micromips")
+      .flag("+m32").flag("+mmicromips");
+
+    Multilib MArchDefault = Multilib()
+      .flag("-mips16").flag("-mmicromips");
+
+    Multilib SoftFloat = Multilib()
+      .gccSuffix("/soft-float")
+      .osSuffix("/soft-float")
+      .includeSuffix("/soft-float")
+      .flag("+msoft-float");
+
+    Multilib Nan2008 = Multilib()
+      .gccSuffix("/nan2008")
+      .osSuffix("/nan2008")
+      .includeSuffix("/nan2008")
+      .flag("+mnan=2008");
+
+    Multilib DefaultFloat = Multilib()
+      .flag("-msoft-float").flag("-mnan=2008");
+
+    Multilib LittleEndian = Multilib()
+      .gccSuffix("/el")
+      .osSuffix("/el")
+      .includeSuffix("/el")
+      .flag("+EL").flag("-EB");
+
+    // Note that this one's osSuffix is ""
+    Multilib MAbi64 = Multilib()
+      .gccSuffix("/64")
+      .includeSuffix("/64")
+      .flag("+mabi=64").flag("-mabi=n32").flag("-m32");
+
+    CSMipsMultilibs = MultilibSet()
+      .Either(MArchMips16, MArchMicroMips, MArchDefault)
+      .Either(SoftFloat, Nan2008, DefaultFloat)
+      .FilterOut("/micromips/nan2008")
+      .FilterOut("/mips16/nan2008")
+      .Maybe(LittleEndian)
+      .Maybe(MAbi64)
+      .FilterOut("/mips16.*/64")
+      .FilterOut("/micromips.*/64")
+      .FilterOut(NonExistant);
+  }
+
+  MultilibSet AndroidMipsMultilibs = MultilibSet()
+    .Maybe(Multilib("/mips-r2").flag("+march=mips32r2"))
+    .FilterOut(NonExistant);
+
+  MultilibSet DebianMipsMultilibs;
+  {
+    Multilib MAbiN32 = Multilib()
+      .gccSuffix("/n32")
+      .includeSuffix("/n32")
+      .flag("+mabi=n32");
+
+    Multilib M64 = Multilib()
+      .gccSuffix("/64")
+      .includeSuffix("/64")
+      .flag("+m64").flag("-m32").flag("-mabi=n32");
+
+    Multilib M32 = Multilib()
+      .flag("-m64").flag("+m32").flag("-mabi=n32");
+
+    DebianMipsMultilibs = MultilibSet()
+      .Either(M32, M64, MAbiN32)
+      .FilterOut(NonExistant);
+  }
+
+  Multilibs.clear();
+
+  // Decide which MultilibSet matches best for the given path
+  // (we do this rather than combining them all because there is a
+  //  a bit of overlap in the directories that each specifies)
+  if (TargetTriple.getEnvironment() == llvm::Triple::Android)
+    Multilibs.combineWith(AndroidMipsMultilibs);
+  else if (DebianMipsMultilibs.size() == 3) {
+    Multilibs.combineWith(DebianMipsMultilibs);
+    BiarchSibling = Multilib();
+  } else if (FSFMipsMultilibs.size() > CSMipsMultilibs.size())
+    Multilibs.combineWith(FSFMipsMultilibs);
+  else
+    Multilibs.combineWith(CSMipsMultilibs);
+
+  llvm::Triple::ArchType TargetArch = TargetTriple.getArch();
+
+  Multilib::flags_list Flags;
+  addMultilibFlag(isMips32(TargetArch), "m32", Flags);
+  addMultilibFlag(isMips64(TargetArch), "m64", Flags);
+  addMultilibFlag(isMips16(Args), "mips16", Flags);
+  addMultilibFlag(isMips32r2(Args), "march=mips32r2", Flags);
+  addMultilibFlag(isMips64r2(Args), "march=mips64r2", Flags);
+  addMultilibFlag(isMicroMips(Args), "mmicromips", Flags);
+  addMultilibFlag(isMipsFP64(Args), "mfp64", Flags);
+  addMultilibFlag(!isMipsFP64(Args), "mfp32", Flags);
+  addMultilibFlag(isMipsNan2008(Args), "mnan=2008", Flags);
+  addMultilibFlag(tools::mips::hasMipsAbiArg(Args, "n32"), "mabi=n32", Flags);
+  // Default is to assume mabi=64
+  bool IsMABI64 =
+      tools::mips::hasMipsAbiArg(Args, "64") ||
+      (!tools::mips::hasMipsAbiArg(Args, "n32") && isMips64(TargetArch));
+  addMultilibFlag(IsMABI64, "mabi=64", Flags);
+  addMultilibFlag(isSoftFloatABI(Args), "msoft-float", Flags);
+  addMultilibFlag(isSoftFloatABI(Args), "mfloat-abi=soft", Flags);
+  addMultilibFlag(!isSoftFloatABI(Args), "mhard-float", Flags);
+  addMultilibFlag(!isSoftFloatABI(Args), "mfloat-abi=hard", Flags);
+  addMultilibFlag(isMipsEL(TargetArch), "EL", Flags);
+  addMultilibFlag(isMipsEB(TargetArch), "EB", Flags);
+
+  return Multilibs.select(Flags, SelectedMultilib);
+}
+
+bool Generic_GCC::GCCInstallationDetector::findBiarchMultilibs(
+    const llvm::Triple &TargetTriple, StringRef Path, const ArgList &Args,
+    bool NeedsBiarchSuffix) {
+
+  // Some versions of SUSE and Fedora on ppc64 put 32-bit libs
+  // in what would normally be GCCInstallPath and put the 64-bit
+  // libs in a subdirectory named 64. The simple logic we follow is that
+  // *if* there is a subdirectory of the right name with crtbegin.o in it,
+  // we use that. If not, and if not a biarch triple alias, we look for
+  // crtbegin.o without the subdirectory.
+
+  Multilib Default;
+  Multilib Alt64 = Multilib()
+    .gccSuffix("/64")
+    .includeSuffix("/64")
+    .flag("-m32").flag("+m64");
+  Multilib Alt32 = Multilib()
+    .gccSuffix("/32")
+    .includeSuffix("/32")
+    .flag("+m32").flag("-m64");
+
+  FilterNonExistant NonExistant(Path);
+
+  // Decide whether the default multilib is 32bit, correcting for
+  // when the default multilib and the alternate appear backwards
+  bool DefaultIs32Bit;
+  if (TargetTriple.isArch32Bit() && !NonExistant(Alt32))
+    DefaultIs32Bit = false;
+  else if (TargetTriple.isArch64Bit() && !NonExistant(Alt64))
+    DefaultIs32Bit = true;
+  else {
+    if (NeedsBiarchSuffix)
+      DefaultIs32Bit = TargetTriple.isArch64Bit();
     else
-      Suffix += hasMipsABIArg(Args, "n32") ? "/mips64" : "/mips64/64";
+      DefaultIs32Bit = TargetTriple.isArch32Bit();
   }
 
-  if (TargetArch == llvm::Triple::mipsel ||
-      TargetArch == llvm::Triple::mips64el)
-    Suffix += "/el";
+  if (DefaultIs32Bit)
+    Default.flag("+m32").flag("-m64");
+  else
+    Default.flag("-m32").flag("+m64");
 
-  if (isSoftFloatABI(Args))
-    Suffix += "/sof";
-  else {
-    if (isMipsFP64(Args))
-      Suffix += "/fp64";
+  Multilibs.push_back(Default);
+  Multilibs.push_back(Alt64);
+  Multilibs.push_back(Alt32);
 
-    if (isMipsNan2008(Args))
-      Suffix += "/nan2008";
-  }
+  Multilibs.FilterOut(NonExistant);
 
-  if (hasCrtBeginObj(Path + Suffix))
-    return;
+  Multilib::flags_list Flags;
+  addMultilibFlag(TargetTriple.isArch64Bit(), "m64", Flags);
+  addMultilibFlag(TargetTriple.isArch32Bit(), "m32", Flags);
 
-  // Check Code Sourcery Toolchain path
-  Suffix.clear();
-  if (isMips16(Args))
-    Suffix += "/mips16";
-  else if (isMicroMips(Args))
-    Suffix += "/micromips";
-
-  if (isSoftFloatABI(Args))
-    Suffix += "/soft-float";
-  else if (isMipsNan2008(Args))
-    Suffix += "/nan2008";
-
-  if (TargetArch == llvm::Triple::mipsel ||
-      TargetArch == llvm::Triple::mips64el)
-    Suffix += "/el";
+  if (!Multilibs.select(Flags, SelectedMultilib))
+    return false;
 
-  if (hasCrtBeginObj(Path + Suffix))
-    return;
+  if (SelectedMultilib == Alt64 || SelectedMultilib == Alt32) {
+    BiarchSibling = Default;
+  }
 
-  Suffix.clear();
+  return true;
 }
 
 void Generic_GCC::GCCInstallationDetector::ScanLibDirForGCCTriple(
-    llvm::Triple::ArchType TargetArch, const ArgList &Args,
+    const llvm::Triple &TargetTriple, const ArgList &Args,
     const std::string &LibDir, StringRef CandidateTriple,
     bool NeedsBiarchSuffix) {
+  llvm::Triple::ArchType TargetArch = TargetTriple.getArch();
   // There are various different suffixes involving the triple we
   // check for. We also record what is necessary to walk from each back
   // up to the lib directory.
@@ -1711,28 +1943,18 @@ void Generic_GCC::GCCInstallationDetecto
       if (CandidateVersion <= Version)
         continue;
 
-      std::string MIPSABIDirSuffix;
-      if (isMipsArch(TargetArch))
-        findMIPSABIDirSuffix(MIPSABIDirSuffix, TargetArch, LI->path(), Args);
-
-      // Some versions of SUSE and Fedora on ppc64 put 32-bit libs
-      // in what would normally be GCCInstallPath and put the 64-bit
-      // libs in a subdirectory named 64. The simple logic we follow is that
-      // *if* there is a subdirectory of the right name with crtbegin.o in it,
-      // we use that. If not, and if not a biarch triple alias, we look for
-      // crtbegin.o without the subdirectory.
-
-      std::string BiarchSuffix;
-      if (findTargetBiarchSuffix(BiarchSuffix,
-                                 LI->path() + MIPSABIDirSuffix,
-                                 TargetArch, Args)) {
-        GCCBiarchSuffix = BiarchSuffix;
-      } else if (NeedsBiarchSuffix ||
-                 !hasCrtBeginObj(LI->path() + MIPSABIDirSuffix)) {
+      Multilibs.clear();
+      SelectedMultilib = Multilib();
+      BiarchSibling.reset();
+
+      // Debian mips multilibs behave more like the rest of the biarch ones,
+      // so handle them there
+      if (isMipsArch(TargetArch)) {
+        if (!findMIPSMultilibs(TargetTriple, LI->path(), Args))
+          continue;
+      } else if (!findBiarchMultilibs(TargetTriple, LI->path(), Args,
+                                      NeedsBiarchSuffix))
         continue;
-      } else {
-        GCCBiarchSuffix.clear();
-      }
 
       Version = CandidateVersion;
       GCCTriple.setTriple(CandidateTriple);
@@ -1741,7 +1963,6 @@ void Generic_GCC::GCCInstallationDetecto
       // Linux.
       GCCInstallPath = LibDir + LibSuffixes[i] + "/" + VersionText.str();
       GCCParentLibPath = GCCInstallPath + InstallSuffixes[i];
-      GCCMIPSABIDirSuffix = MIPSABIDirSuffix;
       IsValid = true;
     }
   }
@@ -2250,9 +2471,9 @@ NetBSD::NetBSD(const Driver &D, const ll
       break;
     case llvm::Triple::mips64:
     case llvm::Triple::mips64el:
-      if (hasMipsABIArg(Args, "o32"))
+      if (tools::mips::hasMipsAbiArg(Args, "o32"))
         getFilePaths().push_back("=/usr/lib/o32");
-      else if (hasMipsABIArg(Args, "64"))
+      else if (tools::mips::hasMipsAbiArg(Args, "64"))
         getFilePaths().push_back("=/usr/lib/64");
       break;
     default:
@@ -2566,25 +2787,24 @@ static void addPathIfExists(Twine Path,
   if (llvm::sys::fs::exists(Path)) Paths.push_back(Path.str());
 }
 
-static StringRef getMultilibDir(const llvm::Triple &Triple,
-                                const ArgList &Args) {
+static StringRef getOSLibDir(const llvm::Triple &Triple, const ArgList &Args) {
   if (isMipsArch(Triple.getArch())) {
     // lib32 directory has a special meaning on MIPS targets.
     // It contains N32 ABI binaries. Use this folder if produce
     // code for N32 ABI only.
-    if (hasMipsABIArg(Args, "n32"))
+    if (tools::mips::hasMipsAbiArg(Args, "n32"))
       return "lib32";
     return Triple.isArch32Bit() ? "lib" : "lib64";
   }
 
-  // It happens that only x86 and PPC use the 'lib32' variant of multilib, and
+  // It happens that only x86 and PPC use the 'lib32' variant of oslibdir, and
   // using that variant while targeting other architectures causes problems
   // because the libraries are laid out in shared system roots that can't cope
-  // with a 'lib32' multilib search path being considered. So we only enable
+  // with a 'lib32' library search path being considered. So we only enable
   // them when we know we may need it.
   //
   // FIXME: This is a bit of a hack. We should really unify this code for
-  // reasoning about multilib spellings with the lib dir spellings in the
+  // reasoning about oslibdir spellings with the lib dir spellings in the
   // GCCInstallationDetector, but that is a more significant refactoring.
   if (Triple.getArch() == llvm::Triple::x86 ||
       Triple.getArch() == llvm::Triple::ppc)
@@ -2596,6 +2816,7 @@ static StringRef getMultilibDir(const ll
 Linux::Linux(const Driver &D, const llvm::Triple &Triple, const ArgList &Args)
   : Generic_ELF(D, Triple, Args) {
   GCCInstallation.init(D, Triple, Args);
+  Multilibs = GCCInstallation.getMultilibs();
   llvm::Triple::ArchType Arch = Triple.getArch();
   std::string SysRoot = computeSysRoot();
 
@@ -2663,29 +2884,20 @@ Linux::Linux(const Driver &D, const llvm
   // to the link paths.
   path_list &Paths = getFilePaths();
 
-  const std::string Multilib = getMultilibDir(Triple, Args);
+  const std::string OSLibDir = getOSLibDir(Triple, Args);
   const std::string MultiarchTriple = getMultiarchTriple(Triple, SysRoot);
 
   // Add the multilib suffixed paths where they are available.
   if (GCCInstallation.isValid()) {
     const llvm::Triple &GCCTriple = GCCInstallation.getTriple();
     const std::string &LibPath = GCCInstallation.getParentLibPath();
+    const Multilib &Multilib = GCCInstallation.getMultilib();
 
     // Sourcery CodeBench MIPS toolchain holds some libraries under
     // a biarch-like suffix of the GCC installation.
-    //
-    // FIXME: It would be cleaner to model this as a variant of bi-arch. IE,
-    // instead of a '64' biarch suffix it would be 'el' or something.
-    if (IsAndroid && IsMips && isMips32r2(Args)) {
-      assert(GCCInstallation.getBiarchSuffix().empty() &&
-             "Unexpected bi-arch suffix");
-      addPathIfExists(GCCInstallation.getInstallPath() + "/mips-r2", Paths);
-    } else {
-      addPathIfExists((GCCInstallation.getInstallPath() +
-                       GCCInstallation.getMIPSABIDirSuffix() +
-                       GCCInstallation.getBiarchSuffix()),
-                      Paths);
-    }
+    addPathIfExists((GCCInstallation.getInstallPath() +
+                     Multilib.gccSuffix()),
+                    Paths);
 
     // GCC cross compiling toolchains will install target libraries which ship
     // as part of the toolchain under <prefix>/<triple>/<libdir> rather than as
@@ -2705,8 +2917,8 @@ Linux::Linux(const Driver &D, const llvm
     //
     // Note that this matches the GCC behavior. See the below comment for where
     // Clang diverges from GCC's behavior.
-    addPathIfExists(LibPath + "/../" + GCCTriple.str() + "/lib/../" + Multilib +
-                    GCCInstallation.getMIPSABIDirSuffix(),
+    addPathIfExists(LibPath + "/../" + GCCTriple.str() + "/lib/../" + OSLibDir +
+                    Multilib.osSuffix(),
                     Paths);
 
     // If the GCC installation we found is inside of the sysroot, we want to
@@ -2720,42 +2932,45 @@ Linux::Linux(const Driver &D, const llvm
     // a bug.
     if (StringRef(LibPath).startswith(SysRoot)) {
       addPathIfExists(LibPath + "/" + MultiarchTriple, Paths);
-      addPathIfExists(LibPath + "/../" + Multilib, Paths);
+      addPathIfExists(LibPath + "/../" + OSLibDir, Paths);
     }
   }
 
   // Similar to the logic for GCC above, if we currently running Clang inside
-  // of the requested system root, add its parent multilib library paths to
+  // of the requested system root, add its parent library paths to
   // those searched.
   // FIXME: It's not clear whether we should use the driver's installed
   // directory ('Dir' below) or the ResourceDir.
   if (StringRef(D.Dir).startswith(SysRoot)) {
     addPathIfExists(D.Dir + "/../lib/" + MultiarchTriple, Paths);
-    addPathIfExists(D.Dir + "/../" + Multilib, Paths);
+    addPathIfExists(D.Dir + "/../" + OSLibDir, Paths);
   }
 
   addPathIfExists(SysRoot + "/lib/" + MultiarchTriple, Paths);
-  addPathIfExists(SysRoot + "/lib/../" + Multilib, Paths);
+  addPathIfExists(SysRoot + "/lib/../" + OSLibDir, Paths);
   addPathIfExists(SysRoot + "/usr/lib/" + MultiarchTriple, Paths);
-  addPathIfExists(SysRoot + "/usr/lib/../" + Multilib, Paths);
+  addPathIfExists(SysRoot + "/usr/lib/../" + OSLibDir, Paths);
 
   // Try walking via the GCC triple path in case of biarch or multiarch GCC
   // installations with strange symlinks.
   if (GCCInstallation.isValid()) {
     addPathIfExists(SysRoot + "/usr/lib/" + GCCInstallation.getTriple().str() +
-                    "/../../" + Multilib, Paths);
+                    "/../../" + OSLibDir, Paths);
 
-    // Add the non-multilib suffixed paths (if potentially different).
-    const std::string &LibPath = GCCInstallation.getParentLibPath();
-    const llvm::Triple &GCCTriple = GCCInstallation.getTriple();
-    if (!GCCInstallation.getBiarchSuffix().empty())
+    // Add the 'other' biarch variant path
+    Multilib BiarchSibling;
+    if (GCCInstallation.getBiarchSibling(BiarchSibling)) {
       addPathIfExists(GCCInstallation.getInstallPath() +
-                      GCCInstallation.getMIPSABIDirSuffix(), Paths);
+                      BiarchSibling.gccSuffix(), Paths);
+    }
 
     // See comments above on the multilib variant for details of why this is
     // included even from outside the sysroot.
+    const std::string &LibPath = GCCInstallation.getParentLibPath();
+    const llvm::Triple &GCCTriple = GCCInstallation.getTriple();
+    const Multilib &Multilib = GCCInstallation.getMultilib();
     addPathIfExists(LibPath + "/../" + GCCTriple.str() +
-                    "/lib" + GCCInstallation.getMIPSABIDirSuffix(), Paths);
+                    "/lib" + Multilib.osSuffix(), Paths);
 
     // See comments above on the multilib variant for details of why this is
     // only included from within the sysroot.
@@ -2804,15 +3019,15 @@ std::string Linux::computeSysRoot() cons
 
   const StringRef InstallDir = GCCInstallation.getInstallPath();
   const StringRef TripleStr = GCCInstallation.getTriple().str();
-  const StringRef MIPSABIDirSuffix = GCCInstallation.getMIPSABIDirSuffix();
+  const Multilib &Multilib = GCCInstallation.getMultilib();
 
   std::string Path = (InstallDir + "/../../../../" + TripleStr + "/libc" +
-                      MIPSABIDirSuffix).str();
+                      Multilib.osSuffix()).str();
 
   if (llvm::sys::fs::exists(Path))
     return Path;
 
-  Path = (InstallDir + "/../../../../sysroot" + MIPSABIDirSuffix).str();
+  Path = (InstallDir + "/../../../../sysroot" + Multilib.osSuffix()).str();
 
   if (llvm::sys::fs::exists(Path))
     return Path;
@@ -2968,17 +3183,16 @@ void Linux::AddClangSystemIncludeArgs(co
 /// libstdc++ installation.
 /*static*/ bool Linux::addLibStdCXXIncludePaths(Twine Base, Twine Suffix,
                                                 Twine TargetArchDir,
-                                                Twine BiarchSuffix,
-                                                Twine MIPSABIDirSuffix,
+                                                Twine IncludeSuffix,
                                                 const ArgList &DriverArgs,
                                                 ArgStringList &CC1Args) {
   if (!addLibStdCXXIncludePaths(Base + Suffix,
-                                TargetArchDir + MIPSABIDirSuffix + BiarchSuffix,
+                                TargetArchDir + IncludeSuffix,
                                 DriverArgs, CC1Args))
     return false;
 
   addSystemInclude(DriverArgs, CC1Args, Base + "/" + TargetArchDir + Suffix
-                   + MIPSABIDirSuffix + BiarchSuffix);
+                   + IncludeSuffix);
   return true;
 }
 
@@ -3022,13 +3236,12 @@ void Linux::AddClangCXXStdlibIncludeArgs
   StringRef LibDir = GCCInstallation.getParentLibPath();
   StringRef InstallDir = GCCInstallation.getInstallPath();
   StringRef TripleStr = GCCInstallation.getTriple().str();
-  StringRef MIPSABIDirSuffix = GCCInstallation.getMIPSABIDirSuffix();
-  StringRef BiarchSuffix = GCCInstallation.getBiarchSuffix();
+  const Multilib &Multilib = GCCInstallation.getMultilib();
   const GCCVersion &Version = GCCInstallation.getVersion();
 
   if (addLibStdCXXIncludePaths(LibDir.str() + "/../include",
-                               "/c++/" + Version.Text, TripleStr, BiarchSuffix,
-                               MIPSABIDirSuffix, DriverArgs, CC1Args))
+                               "/c++/" + Version.Text, TripleStr,
+                               Multilib.includeSuffix(), DriverArgs, CC1Args))
     return;
 
   const std::string LibStdCXXIncludePathCandidates[] = {
@@ -3047,7 +3260,7 @@ void Linux::AddClangCXXStdlibIncludeArgs
   for (unsigned i = 0; i < llvm::array_lengthof(LibStdCXXIncludePathCandidates);
        ++i) {
     if (addLibStdCXXIncludePaths(LibStdCXXIncludePathCandidates[i],
-                                 TripleStr + MIPSABIDirSuffix + BiarchSuffix,
+                                 TripleStr + Multilib.includeSuffix(),
                                  DriverArgs, CC1Args))
       break;
   }

Modified: cfe/trunk/lib/Driver/ToolChains.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Driver/ToolChains.h?rev=201202&r1=201201&r2=201202&view=diff
==============================================================================
--- cfe/trunk/lib/Driver/ToolChains.h (original)
+++ cfe/trunk/lib/Driver/ToolChains.h Tue Feb 11 19:29:25 2014
@@ -13,8 +13,10 @@
 #include "Tools.h"
 #include "clang/Basic/VersionTuple.h"
 #include "clang/Driver/Action.h"
+#include "clang/Driver/Multilib.h"
 #include "clang/Driver/ToolChain.h"
 #include "llvm/ADT/DenseMap.h"
+#include "llvm/ADT/Optional.h"
 #include "llvm/Support/Compiler.h"
 #include <set>
 #include <vector>
@@ -67,7 +69,6 @@ protected:
     bool operator>=(const GCCVersion &RHS) const { return !(*this < RHS); }
   };
 
-
   /// \brief This is a class to find a viable GCC installation for Clang to
   /// use.
   ///
@@ -80,9 +81,13 @@ protected:
 
     // FIXME: These might be better as path objects.
     std::string GCCInstallPath;
-    std::string GCCBiarchSuffix;
     std::string GCCParentLibPath;
-    std::string GCCMIPSABIDirSuffix;
+
+    /// The primary multilib appropriate for the given flags.
+    Multilib SelectedMultilib;
+    /// On Biarch systems, this corresponds to the default multilib when
+    /// targeting the non-default multilib. Otherwise, it is empty.
+    llvm::Optional<Multilib> BiarchSibling;
 
     GCCVersion Version;
 
@@ -90,6 +95,9 @@ protected:
     // order to print out detailed information in verbose mode.
     std::set<std::string> CandidateGCCInstallPaths;
 
+    /// The set of multilibs that the detected installation supports.
+    MultilibSet Multilibs;
+
   public:
     GCCInstallationDetector() : IsValid(false) {}
     void init(const Driver &D, const llvm::Triple &TargetTriple,
@@ -104,26 +112,18 @@ protected:
     /// \brief Get the detected GCC installation path.
     StringRef getInstallPath() const { return GCCInstallPath; }
 
-    /// \brief Get the detected GCC installation path suffix for the bi-arch
-    /// target variant.
-    StringRef getBiarchSuffix() const { return GCCBiarchSuffix; }
-
     /// \brief Get the detected GCC parent lib path.
     StringRef getParentLibPath() const { return GCCParentLibPath; }
 
-    /// \brief Get the detected GCC MIPS ABI directory suffix.
-    ///
-    /// This is used as a suffix both to the install directory of GCC and as
-    /// a suffix to its parent lib path in order to select a MIPS ABI-specific
-    /// subdirectory.
-    ///
-    /// This will always be empty for any non-MIPS target.
-    ///
-    // FIXME: This probably shouldn't exist at all, and should be factored
-    // into the multiarch and/or biarch support. Please don't add more uses of
-    // this interface, it is meant as a legacy crutch for the MIPS driver
-    // logic.
-    StringRef getMIPSABIDirSuffix() const { return GCCMIPSABIDirSuffix; }
+    /// \brief Get the detected Multilib
+    const Multilib &getMultilib() const { return SelectedMultilib; }
+
+    /// \brief Get the whole MultilibSet
+    const MultilibSet &getMultilibs() const { return Multilibs; }
+
+    /// Get the biarch sibling multilib (if it exists).
+    /// \return true iff such a sibling exists
+    bool getBiarchSibling(Multilib &M) const;
 
     /// \brief Get the detected GCC version string.
     const GCCVersion &getVersion() const { return Version; }
@@ -140,15 +140,18 @@ protected:
                              SmallVectorImpl<StringRef> &BiarchLibDirs,
                              SmallVectorImpl<StringRef> &BiarchTripleAliases);
 
-    void ScanLibDirForGCCTriple(llvm::Triple::ArchType TargetArch,
+    void ScanLibDirForGCCTriple(const llvm::Triple &TargetArch,
                                 const llvm::opt::ArgList &Args,
                                 const std::string &LibDir,
                                 StringRef CandidateTriple,
                                 bool NeedsBiarchSuffix = false);
 
-    void findMIPSABIDirSuffix(std::string &Suffix,
-                              llvm::Triple::ArchType TargetArch, StringRef Path,
-                              const llvm::opt::ArgList &Args);
+    bool findMIPSMultilibs(const llvm::Triple &TargetArch, StringRef Path,
+                           const llvm::opt::ArgList &Args);
+
+    bool findBiarchMultilibs(const llvm::Triple &TargetArch, StringRef Path,
+                             const llvm::opt::ArgList &Args,
+                             bool NeedsBiarchSuffix);
   };
 
   GCCInstallationDetector GCCInstallation;
@@ -665,9 +668,7 @@ protected:
 
 private:
   static bool addLibStdCXXIncludePaths(Twine Base, Twine Suffix,
-                                       Twine TargetArchDir,
-                                       Twine BiarchSuffix,
-                                       Twine MIPSABIDirSuffix,
+                                       Twine TargetArchDir, Twine IncludeSuffix,
                                        const llvm::opt::ArgList &DriverArgs,
                                        llvm::opt::ArgStringList &CC1Args);
   static bool addLibStdCXXIncludePaths(Twine Base, Twine TargetArchDir,

Modified: cfe/trunk/lib/Driver/Tools.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Driver/Tools.cpp?rev=201202&r1=201201&r2=201202&view=diff
==============================================================================
--- cfe/trunk/lib/Driver/Tools.cpp (original)
+++ cfe/trunk/lib/Driver/Tools.cpp Tue Feb 11 19:29:25 2014
@@ -44,11 +44,6 @@ using namespace clang::driver::tools;
 using namespace clang;
 using namespace llvm::opt;
 
-static bool hasMipsABIArg(const ArgList &Args, const char *Value) {
-  Arg *A = Args.getLastArg(options::OPT_mabi_EQ);
-  return A && (A->getValue() == StringRef(Value));
-}
-
 /// CheckPreprocessingOptions - Perform some validation of preprocessing
 /// arguments that is shared with gcc.
 static void CheckPreprocessingOptions(const Driver &D, const ArgList &Args) {
@@ -480,7 +475,7 @@ static bool isSignedCharDefault(const ll
   case llvm::Triple::arm:
   case llvm::Triple::ppc:
   case llvm::Triple::ppc64:
-    if (Triple.isOSBinFormatMachO())
+    if (Triple.isOSDarwin())
       return true;
     return false;
 
@@ -751,7 +746,7 @@ void Clang::AddARMTargetArgs(const ArgLi
   const char *ABIName = 0;
   if (Arg *A = Args.getLastArg(options::OPT_mabi_EQ)) {
     ABIName = A->getValue();
-  } else if (Triple.isOSBinFormatMachO()) {
+  } else if (Triple.isOSDarwin()) {
     // The backend is hardwired to assume AAPCS for M-class processors, ensure
     // the frontend matches that.
     if (Triple.getEnvironment() == llvm::Triple::EABI ||
@@ -1197,7 +1192,7 @@ static const char *getX86TargetCPU(const
                                    const llvm::Triple &Triple) {
   if (const Arg *A = Args.getLastArg(options::OPT_march_EQ)) {
     if (StringRef(A->getValue()) != "native") {
-      if (Triple.isOSBinFormatMachO() && Triple.getArchName() == "x86_64h")
+      if (Triple.isOSDarwin() && Triple.getArchName() == "x86_64h")
         return "core-avx2";
 
       return A->getValue();
@@ -1222,7 +1217,7 @@ static const char *getX86TargetCPU(const
   bool Is64Bit = Triple.getArch() == llvm::Triple::x86_64;
 
   // FIXME: Need target hooks.
-  if (Triple.isOSBinFormatMachO()) {
+  if (Triple.isOSDarwin()) {
     if (Triple.getArchName() == "x86_64h")
       return "core-avx2";
     return Is64Bit ? "core2" : "yonah";
@@ -2624,7 +2619,7 @@ void Clang::ConstructJob(Compilation &C,
       // -gline-tables-only.
       CmdArgs.push_back("-gline-tables-only");
       // Default is dwarf-2 for darwin.
-      if (getToolChain().getTriple().isOSBinFormatMachO())
+      if (getToolChain().getTriple().isOSDarwin())
         CmdArgs.push_back("-gdwarf-2");
     } else if (A->getOption().matches(options::OPT_gdwarf_2))
       CmdArgs.push_back("-gdwarf-2");
@@ -2635,7 +2630,7 @@ void Clang::ConstructJob(Compilation &C,
     else if (!A->getOption().matches(options::OPT_g0) &&
              !A->getOption().matches(options::OPT_ggdb0)) {
       // Default is dwarf-2 for darwin.
-      if (getToolChain().getTriple().isOSBinFormatMachO())
+      if (getToolChain().getTriple().isOSDarwin())
         CmdArgs.push_back("-gdwarf-2");
       else
         CmdArgs.push_back("-g");
@@ -3949,7 +3944,7 @@ ObjCRuntime Clang::AddObjCRuntimeArgs(co
   // -fnext-runtime
   } else if (runtimeArg->getOption().matches(options::OPT_fnext_runtime)) {
     // On Darwin, make this use the default behavior for the toolchain.
-    if (getToolChain().getTriple().isOSBinFormatMachO()) {
+    if (getToolChain().getTriple().isOSDarwin()) {
       runtime = getToolChain().getDefaultObjCRuntime(isNonFragile);
 
     // Otherwise, build for a generic macosx port.
@@ -4223,7 +4218,7 @@ void gcc::Common::ConstructJob(Compilati
 
   // If using a driver driver, force the arch.
   llvm::Triple::ArchType Arch = getToolChain().getArch();
-  if (getToolChain().getTriple().isOSBinFormatMachO()) {
+  if (getToolChain().getTriple().isOSDarwin()) {
     CmdArgs.push_back("-arch");
 
     // FIXME: Remove these special cases.
@@ -4718,6 +4713,11 @@ const char *arm::getLLVMArchSuffixForARM
     .Default("");
 }
 
+bool mips::hasMipsAbiArg(const ArgList &Args, const char *Value) {
+  Arg *A = Args.getLastArg(options::OPT_mabi_EQ);
+  return A && (A->getValue() == StringRef(Value));
+}
+
 llvm::Triple::ArchType darwin::getArchTypeForMachOArchName(StringRef Str) {
   // See arch(3) and llvm-gcc's driver-driver.c. We don't implement support for
   // archs which Darwin doesn't use.
@@ -6192,13 +6192,13 @@ void netbsd::Link::ConstructJob(Compilat
     break;
   case llvm::Triple::mips64:
   case llvm::Triple::mips64el:
-    if (hasMipsABIArg(Args, "32")) {
+    if (mips::hasMipsAbiArg(Args, "32")) {
       CmdArgs.push_back("-m");
       if (getToolChain().getArch() == llvm::Triple::mips64)
         CmdArgs.push_back("elf32btsmip");
       else
         CmdArgs.push_back("elf32ltsmip");
-   } else if (hasMipsABIArg(Args, "64")) {
+   } else if (mips::hasMipsAbiArg(Args, "64")) {
      CmdArgs.push_back("-m");
      if (getToolChain().getArch() == llvm::Triple::mips64)
        CmdArgs.push_back("elf64btsmip");
@@ -6502,7 +6502,7 @@ static StringRef getLinuxDynamicLinker(c
     return "/lib/ld.so.1";
   else if (ToolChain.getArch() == llvm::Triple::mips64 ||
            ToolChain.getArch() == llvm::Triple::mips64el) {
-    if (hasMipsABIArg(Args, "n32"))
+    if (mips::hasMipsAbiArg(Args, "n32"))
       return "/lib32/ld.so.1";
     else
       return "/lib64/ld.so.1";
@@ -6585,13 +6585,13 @@ void gnutools::Link::ConstructJob(Compil
   else if (ToolChain.getArch() == llvm::Triple::mipsel)
     CmdArgs.push_back("elf32ltsmip");
   else if (ToolChain.getArch() == llvm::Triple::mips64) {
-    if (hasMipsABIArg(Args, "n32"))
+    if (mips::hasMipsAbiArg(Args, "n32"))
       CmdArgs.push_back("elf32btsmipn32");
     else
       CmdArgs.push_back("elf64btsmip");
   }
   else if (ToolChain.getArch() == llvm::Triple::mips64el) {
-    if (hasMipsABIArg(Args, "n32"))
+    if (mips::hasMipsAbiArg(Args, "n32"))
       CmdArgs.push_back("elf32ltsmipn32");
     else
       CmdArgs.push_back("elf64ltsmip");

Modified: cfe/trunk/lib/Driver/Tools.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Driver/Tools.h?rev=201202&r1=201201&r2=201202&view=diff
==============================================================================
--- cfe/trunk/lib/Driver/Tools.h (original)
+++ cfe/trunk/lib/Driver/Tools.h Tue Feb 11 19:29:25 2014
@@ -209,6 +209,10 @@ namespace arm {
   const char* getLLVMArchSuffixForARM(StringRef CPU);
 }
 
+namespace mips {
+  bool hasMipsAbiArg(const llvm::opt::ArgList &Args, const char *Value);
+}
+
 namespace darwin {
   llvm::Triple::ArchType getArchTypeForMachOArchName(StringRef Str);
   void setTripleTypeForMachOArchName(llvm::Triple &T, StringRef Str);

Modified: cfe/trunk/unittests/CMakeLists.txt
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/unittests/CMakeLists.txt?rev=201202&r1=201201&r2=201202&view=diff
==============================================================================
--- cfe/trunk/unittests/CMakeLists.txt (original)
+++ cfe/trunk/unittests/CMakeLists.txt Tue Feb 11 19:29:25 2014
@@ -11,6 +11,7 @@ endfunction()
 
 add_subdirectory(Basic)
 add_subdirectory(Lex)
+add_subdirectory(Driver)
 if(CLANG_ENABLE_STATIC_ANALYZER)
   add_subdirectory(Frontend)
 endif()

Added: cfe/trunk/unittests/Driver/CMakeLists.txt
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/unittests/Driver/CMakeLists.txt?rev=201202&view=auto
==============================================================================
--- cfe/trunk/unittests/Driver/CMakeLists.txt (added)
+++ cfe/trunk/unittests/Driver/CMakeLists.txt Tue Feb 11 19:29:25 2014
@@ -0,0 +1,11 @@
+set(LLVM_LINK_COMPONENTS
+  Support
+  )
+
+add_clang_unittest(DriverTests
+  MultilibTest.cpp
+  )
+
+target_link_libraries(DriverTests
+  clangDriver
+  )

Added: cfe/trunk/unittests/Driver/Makefile
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/unittests/Driver/Makefile?rev=201202&view=auto
==============================================================================
--- cfe/trunk/unittests/Driver/Makefile (added)
+++ cfe/trunk/unittests/Driver/Makefile Tue Feb 11 19:29:25 2014
@@ -0,0 +1,16 @@
+##===- unittests/Driver/Makefile ---------------------------*- Makefile -*-===##
+#
+#                     The LLVM Compiler Infrastructure
+#
+# This file is distributed under the University of Illinois Open Source
+# License. See LICENSE.TXT for details.
+#
+##===----------------------------------------------------------------------===##
+
+CLANG_LEVEL = ../..
+TESTNAME = Multilib
+include $(CLANG_LEVEL)/../../Makefile.config
+LINK_COMPONENTS := $(TARGETS_TO_BUILD) support option
+USEDLIBS = clangDriver.a clangBasic.a
+
+include $(CLANG_LEVEL)/unittests/Makefile

Added: cfe/trunk/unittests/Driver/MultilibTest.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/unittests/Driver/MultilibTest.cpp?rev=201202&view=auto
==============================================================================
--- cfe/trunk/unittests/Driver/MultilibTest.cpp (added)
+++ cfe/trunk/unittests/Driver/MultilibTest.cpp Tue Feb 11 19:29:25 2014
@@ -0,0 +1,356 @@
+//===- unittests/Driver/MultilibTest.cpp --- Multilib tests ---------------===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// Unit tests for Multilib and MultilibSet
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang/Driver/Multilib.h"
+#include "clang/Basic/LLVM.h"
+#include "gtest/gtest.h"
+#include "llvm/ADT/StringSwitch.h"
+#include "llvm/ADT/StringRef.h"
+
+using namespace clang::driver;
+using namespace clang;
+
+TEST(MultilibTest, MultilibValidity) {
+
+  ASSERT_TRUE(Multilib().isValid()) << "Empty multilib is not valid";
+
+  ASSERT_TRUE(Multilib().flag("+foo").isValid())
+      << "Single indicative flag is not valid";
+
+  ASSERT_TRUE(Multilib().flag("-foo").isValid())
+      << "Single contraindicative flag is not valid";
+
+  ASSERT_FALSE(Multilib().flag("+foo").flag("-foo").isValid())
+      << "Conflicting flags should invalidate the Multilib";
+
+  ASSERT_TRUE(Multilib().flag("+foo").flag("+foo").isValid())
+      << "Multilib should be valid even if it has the same flag twice";
+
+  ASSERT_TRUE(Multilib().flag("+foo").flag("-foobar").isValid())
+      << "Seemingly conflicting prefixes shouldn't actually conflict";
+}
+
+TEST(MultilibTest, OpEqReflexivity1) {
+  Multilib M;
+  ASSERT_TRUE(M == M) << "Multilib::operator==() is not reflexive";
+}
+
+TEST(MultilibTest, OpEqReflexivity2) {
+  ASSERT_TRUE(Multilib() == Multilib())
+      << "Separately constructed default multilibs are not equal";
+}
+
+TEST(MultilibTest, OpEqReflexivity3) {
+  Multilib M1, M2;
+  M1.flag("+foo");
+  M2.flag("+foo");
+  ASSERT_TRUE(M1 == M2) << "Multilibs with the same flag should be the same";
+}
+
+TEST(MultilibTest, OpEqInequivalence1) {
+  Multilib M1, M2;
+  M1.flag("+foo");
+  M2.flag("-foo");
+  ASSERT_FALSE(M1 == M2) << "Multilibs with conflicting flags are not the same";
+  ASSERT_FALSE(M2 == M1)
+      << "Multilibs with conflicting flags are not the same (commuted)";
+}
+
+TEST(MultilibTest, OpEqInequivalence2) {
+  Multilib M1, M2;
+  M2.flag("+foo");
+  ASSERT_FALSE(M1 == M2) << "Flags make Multilibs different";
+}
+
+TEST(MultilibTest, OpEqEquivalence1) {
+  Multilib M1, M2;
+  M1.flag("+foo");
+  M2.flag("+foo").flag("+foo");
+  ASSERT_TRUE(M1 == M2) << "Flag duplication shouldn't affect equivalence";
+  ASSERT_TRUE(M2 == M1)
+      << "Flag duplication shouldn't affect equivalence (commuted)";
+}
+
+TEST(MultilibTest, OpEqEquivalence2) {
+  Multilib M1("64");
+  Multilib M2;
+  M2.gccSuffix("/64");
+  ASSERT_TRUE(M1 == M2)
+      << "Constructor argument must match Multilib::gccSuffix()";
+  ASSERT_TRUE(M2 == M1)
+      << "Constructor argument must match Multilib::gccSuffix() (commuted)";
+}
+
+TEST(MultilibTest, OpEqEquivalence3) {
+  Multilib M1("", "32");
+  Multilib M2;
+  M2.osSuffix("/32");
+  ASSERT_TRUE(M1 == M2)
+      << "Constructor argument must match Multilib::osSuffix()";
+  ASSERT_TRUE(M2 == M1)
+      << "Constructor argument must match Multilib::osSuffix() (commuted)";
+}
+
+TEST(MultilibTest, OpEqEquivalence4) {
+  Multilib M1("", "", "16");
+  Multilib M2;
+  M2.includeSuffix("/16");
+  ASSERT_TRUE(M1 == M2)
+      << "Constructor argument must match Multilib::includeSuffix()";
+  ASSERT_TRUE(M2 == M1)
+      << "Constructor argument must match Multilib::includeSuffix() (commuted)";
+}
+
+TEST(MultilibTest, OpEqInequivalence3) {
+  Multilib M1("foo");
+  Multilib M2("bar");
+  ASSERT_FALSE(M1 == M2) << "Differing gccSuffixes should be different";
+  ASSERT_FALSE(M2 == M1)
+      << "Differing gccSuffixes should be different (commuted)";
+}
+
+TEST(MultilibTest, OpEqInequivalence4) {
+  Multilib M1("", "foo");
+  Multilib M2("", "bar");
+  ASSERT_FALSE(M1 == M2) << "Differing osSuffixes should be different";
+  ASSERT_FALSE(M2 == M1)
+      << "Differing osSuffixes should be different (commuted)";
+}
+
+TEST(MultilibTest, OpEqInequivalence5) {
+  Multilib M1("", "", "foo");
+  Multilib M2("", "", "bar");
+  ASSERT_FALSE(M1 == M2) << "Differing includeSuffixes should be different";
+  ASSERT_FALSE(M2 == M1)
+      << "Differing includeSuffixes should be different (commuted)";
+}
+
+TEST(MultilibTest, Construction1) {
+  Multilib M("gcc64", "os64", "inc64");
+  ASSERT_TRUE(M.gccSuffix() == "/gcc64");
+  ASSERT_TRUE(M.osSuffix() == "/os64");
+  ASSERT_TRUE(M.includeSuffix() == "/inc64");
+}
+
+TEST(MultilibTest, Construction2) {
+  Multilib M1;
+  Multilib M2("");
+  Multilib M3("", "");
+  Multilib M4("", "", "");
+  ASSERT_TRUE(M1 == M2)
+      << "Default arguments to Multilib constructor broken (first argument)";
+  ASSERT_TRUE(M1 == M3)
+      << "Default arguments to Multilib constructor broken (second argument)";
+  ASSERT_TRUE(M1 == M4)
+      << "Default arguments to Multilib constructor broken (third argument)";
+}
+
+TEST(MultilibTest, Construction3) {
+  Multilib M = Multilib().flag("+f1").flag("+f2").flag("-f3");
+  for (Multilib::flags_list::const_iterator I = M.flags().begin(),
+                                            E = M.flags().end();
+       I != E; ++I) {
+    ASSERT_TRUE(llvm::StringSwitch<bool>(*I)
+                    .Cases("+f1", "+f2", "-f3", true)
+                    .Default(false));
+  }
+}
+
+static bool hasFlag(const Multilib &M, StringRef Flag) {
+  for (Multilib::flags_list::const_iterator I = M.flags().begin(),
+                                            E = M.flags().end();
+       I != E; ++I) {
+    if (*I == Flag)
+      return true;
+    else if (StringRef(*I).substr(1) == Flag.substr(1))
+      return false;
+  }
+  return false;
+}
+
+TEST(MultilibTest, SetConstruction1) {
+  // Single maybe
+  MultilibSet MS;
+  ASSERT_TRUE(MS.size() == 0);
+  MS.Maybe(Multilib("64").flag("+m64"));
+  ASSERT_TRUE(MS.size() == 2);
+  for (MultilibSet::const_iterator I = MS.begin(), E = MS.end(); I != E; ++I) {
+    if (I->gccSuffix() == "/64")
+      ASSERT_TRUE(I->flags()[0] == "+m64");
+    else if (I->gccSuffix() == "")
+      ASSERT_TRUE(I->flags()[0] == "-m64");
+    else
+      FAIL() << "Unrecognized gccSufix: " << I->gccSuffix();
+  }
+}
+
+TEST(MultilibTest, SetConstruction2) {
+  // Double maybe
+  MultilibSet MS;
+  MS.Maybe(Multilib("sof").flag("+sof"));
+  MS.Maybe(Multilib("el").flag("+EL"));
+  ASSERT_TRUE(MS.size() == 4);
+  for (MultilibSet::const_iterator I = MS.begin(), E = MS.end(); I != E; ++I) {
+    ASSERT_TRUE(I->isValid()) << "Multilb " << *I << " should be valid";
+    ASSERT_TRUE(llvm::StringSwitch<bool>(I->gccSuffix())
+                    .Cases("", "/sof", "/el", "/sof/el", true)
+                    .Default(false))
+        << "Multilib " << *I << " wasn't expected";
+    ASSERT_TRUE(llvm::StringSwitch<bool>(I->gccSuffix())
+                    .Case("", hasFlag(*I, "-sof"))
+                    .Case("/sof", hasFlag(*I, "+sof"))
+                    .Case("/el", hasFlag(*I, "-sof"))
+                    .Case("/sof/el", hasFlag(*I, "+sof"))
+                    .Default(false))
+        << "Multilib " << *I << " didn't have the appropriate {+,-}sof flag";
+    ASSERT_TRUE(llvm::StringSwitch<bool>(I->gccSuffix())
+                    .Case("", hasFlag(*I, "-EL"))
+                    .Case("/sof", hasFlag(*I, "-EL"))
+                    .Case("/el", hasFlag(*I, "+EL"))
+                    .Case("/sof/el", hasFlag(*I, "+EL"))
+                    .Default(false))
+        << "Multilib " << *I << " didn't have the appropriate {+,-}EL flag";
+  }
+}
+
+TEST(MultilibTest, SetPushback) {
+  MultilibSet MS;
+  MS.push_back(Multilib("one"));
+  MS.push_back(Multilib("two"));
+  ASSERT_TRUE(MS.size() == 2);
+  for (MultilibSet::const_iterator I = MS.begin(), E = MS.end(); I != E; ++I) {
+    ASSERT_TRUE(llvm::StringSwitch<bool>(I->gccSuffix())
+                    .Cases("/one", "/two", true)
+                    .Default(false));
+  }
+  MS.clear();
+  ASSERT_TRUE(MS.size() == 0);
+}
+
+TEST(MultilibTest, SetRegexFilter) {
+  MultilibSet MS;
+  MS.Maybe(Multilib("one"));
+  MS.Maybe(Multilib("two"));
+  MS.Maybe(Multilib("three"));
+  ASSERT_EQ(MS.size(), (unsigned)2 * 2 * 2)
+      << "Size before filter was incorrect. Contents:\n" << MS;
+  MS.FilterOut("/one/two/three");
+  ASSERT_EQ(MS.size(), (unsigned)2 * 2 * 2 - 1)
+      << "Size after filter was incorrect. Contents:\n" << MS;
+  for (MultilibSet::const_iterator I = MS.begin(), E = MS.end(); I != E; ++I) {
+    ASSERT_TRUE(I->gccSuffix() != "/one/two/three")
+        << "The filter should have removed " << *I;
+  }
+}
+
+TEST(MultilibTest, SetFilterObject) {
+  // Filter object
+  struct StartsWithP : public MultilibSet::FilterCallback {
+    bool operator()(const Multilib &M) const LLVM_OVERRIDE {
+      return StringRef(M.gccSuffix()).startswith("/p");
+    }
+  };
+  MultilibSet MS;
+  MS.Maybe(Multilib("orange"));
+  MS.Maybe(Multilib("pear"));
+  MS.Maybe(Multilib("plum"));
+  ASSERT_EQ((int)MS.size(), 1 /* Default */ +
+                            1 /* pear */ +
+                            1 /* plum */ +
+                            1 /* pear/plum */ +
+                            1 /* orange */ +
+                            1 /* orange/pear */ +
+                            1 /* orange/plum */ +
+                            1 /* orange/pear/plum */ )
+      << "Size before filter was incorrect. Contents:\n" << MS;
+  MS.FilterOut(StartsWithP());
+  ASSERT_EQ((int)MS.size(), 1 /* Default */ +
+                            1 /* orange */ +
+                            1 /* orange/pear */ +
+                            1 /* orange/plum */ + 
+                            1 /* orange/pear/plum */ )
+      << "Size after filter was incorrect. Contents:\n" << MS;
+  for (MultilibSet::const_iterator I = MS.begin(), E = MS.end(); I != E; ++I) {
+    ASSERT_FALSE(StringRef(I->gccSuffix()).startswith("/p"))
+        << "The filter should have removed " << *I;
+  }
+}
+
+TEST(MultilibTest, SetSelection1) {
+  MultilibSet MS1 = MultilibSet()
+    .Maybe(Multilib("64").flag("+m64"));
+
+  Multilib::flags_list FlagM64;
+  FlagM64.push_back("+m64");
+  Multilib SelectionM64;
+  ASSERT_TRUE(MS1.select(FlagM64, SelectionM64))
+      << "Flag set was {\"+m64\"}, but selection not found";
+  ASSERT_TRUE(SelectionM64.gccSuffix() == "/64")
+      << "Selection picked " << SelectionM64 << " which was not expected";
+
+  Multilib::flags_list FlagNoM64;
+  FlagNoM64.push_back("-m64");
+  Multilib SelectionNoM64;
+  ASSERT_TRUE(MS1.select(FlagNoM64, SelectionNoM64))
+      << "Flag set was {\"-m64\"}, but selection not found";
+  ASSERT_TRUE(SelectionNoM64.gccSuffix() == "")
+      << "Selection picked " << SelectionNoM64 << " which was not expected";
+}
+
+TEST(MultilibTest, SetSelection2) {
+  MultilibSet MS2 = MultilibSet()
+    .Maybe(Multilib("el").flag("+EL"))
+    .Maybe(Multilib("sf").flag("+SF"));
+
+  for (unsigned I = 0; I < 4; ++I) {
+    bool IsEL = I & 0x1;
+    bool IsSF = I & 0x2;
+    Multilib::flags_list Flags;
+    if (IsEL)
+      Flags.push_back("+EL");
+    else
+      Flags.push_back("-EL");
+
+    if (IsSF)
+      Flags.push_back("+SF");
+    else
+      Flags.push_back("-SF");
+
+    Multilib Selection;
+    ASSERT_TRUE(MS2.select(Flags, Selection)) << "Selection failed for "
+                                              << (IsEL ? "+EL" : "-EL") << " "
+                                              << (IsSF ? "+SF" : "-SF");
+
+    std::string Suffix;
+    if (IsEL)
+      Suffix += "/el";
+    if (IsSF)
+      Suffix += "/sf";
+
+    ASSERT_EQ(Selection.gccSuffix(), Suffix) << "Selection picked " << Selection
+                                             << " which was not expected ";
+  }
+}
+
+TEST(MultilibTest, SetCombineWith) {
+  MultilibSet Coffee;
+  Coffee.push_back(Multilib("coffee"));
+  MultilibSet Milk;
+  Milk.push_back(Multilib("milk"));
+  MultilibSet Latte;
+  ASSERT_EQ(Latte.size(), (unsigned)0);
+  Latte.combineWith(Coffee);
+  ASSERT_EQ(Latte.size(), (unsigned)1);
+  Latte.combineWith(Milk);
+  ASSERT_EQ(Latte.size(), (unsigned)2);
+}

Modified: cfe/trunk/unittests/Makefile
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/unittests/Makefile?rev=201202&r1=201201&r2=201202&view=diff
==============================================================================
--- cfe/trunk/unittests/Makefile (original)
+++ cfe/trunk/unittests/Makefile Tue Feb 11 19:29:25 2014
@@ -14,7 +14,7 @@ ifndef CLANG_LEVEL
 
 IS_UNITTEST_LEVEL := 1
 CLANG_LEVEL := ..
-PARALLEL_DIRS = Basic Lex
+PARALLEL_DIRS = Basic Lex Driver
 
 include $(CLANG_LEVEL)/../..//Makefile.config
 





More information about the cfe-commits mailing list