summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJonathan Roelofs <jonathan@codesourcery.com>2014-02-12 01:29:25 +0000
committerJonathan Roelofs <jonathan@codesourcery.com>2014-02-12 01:29:25 +0000
commit1b6afd89e6c1230c6a842701ec5bbc45d81d95b6 (patch)
tree664f09ecb0d83b4d53879a9cc8dc90c0c65a80fd
parent10dbcf4aad39719036dc32b338ac2c111ecd00d8 (diff)
downloadclang-1b6afd89e6c1230c6a842701ec5bbc45d81d95b6.tar.gz
clang-1b6afd89e6c1230c6a842701ec5bbc45d81d95b6.tar.bz2
clang-1b6afd89e6c1230c6a842701ec5bbc45d81d95b6.tar.xz
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 git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@201202 91177308-0d34-0410-b5e6-96231b3b80d8
-rw-r--r--include/clang/Driver/Multilib.h163
-rw-r--r--include/clang/Driver/ToolChain.h5
-rw-r--r--lib/Driver/CMakeLists.txt1
-rw-r--r--lib/Driver/Driver.cpp61
-rw-r--r--lib/Driver/Multilib.cpp347
-rw-r--r--lib/Driver/ToolChains.cpp537
-rw-r--r--lib/Driver/ToolChains.h55
-rw-r--r--lib/Driver/Tools.cpp36
-rw-r--r--lib/Driver/Tools.h4
-rw-r--r--unittests/CMakeLists.txt1
-rw-r--r--unittests/Driver/CMakeLists.txt11
-rw-r--r--unittests/Driver/Makefile16
-rw-r--r--unittests/Driver/MultilibTest.cpp356
-rw-r--r--unittests/Makefile2
14 files changed, 1348 insertions, 247 deletions
diff --git a/include/clang/Driver/Multilib.h b/include/clang/Driver/Multilib.h
new file mode 100644
index 0000000000..ad59d8a57f
--- /dev/null
+++ b/include/clang/Driver/Multilib.h
@@ -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
+
diff --git a/include/clang/Driver/ToolChain.h b/include/clang/Driver/ToolChain.h
index e775f630e1..e8f2c78b73 100644
--- a/include/clang/Driver/ToolChain.h
+++ b/include/clang/Driver/ToolChain.h
@@ -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.
diff --git a/lib/Driver/CMakeLists.txt b/lib/Driver/CMakeLists.txt
index e874ab8a8b..818d554d4e 100644
--- a/lib/Driver/CMakeLists.txt
+++ b/lib/Driver/CMakeLists.txt
@@ -11,6 +11,7 @@ add_clang_library(clangDriver
Driver.cpp
DriverOptions.cpp
Job.cpp
+ Multilib.cpp
Phases.cpp
SanitizerArgs.cpp
Tool.cpp
diff --git a/lib/Driver/Driver.cpp b/lib/Driver/Driver.cpp
index 898bdc94e2..dd650ae833 100644
--- a/lib/Driver/Driver.cpp
+++ b/lib/Driver/Driver.cpp
@@ -748,54 +748,37 @@ bool Driver::HandleImmediateArgs(const Compilation &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;
}
diff --git a/lib/Driver/Multilib.cpp b/lib/Driver/Multilib.cpp
new file mode 100644
index 0000000000..6d68517e83
--- /dev/null
+++ b/lib/Driver/Multilib.cpp
@@ -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;
+}
diff --git a/lib/Driver/ToolChains.cpp b/lib/Driver/ToolChains.cpp
index f215594758..60ca7ea195 100644
--- a/lib/Driver/ToolChains.cpp
+++ b/lib/Driver/ToolChains.cpp
@@ -139,11 +139,11 @@ static const char *GetArmArchForMCpu(StringRef Value) {
}
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::isOlderThan(int RHSMajor, int RHSMinor,
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::init(
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::init(
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::init(
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::GCCInstallationDetector::print(raw_ostream &OS) const {
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::GCCInstallationDetector::print(raw_ostream &OS) const {
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 &Args) {
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::GCCInstallationDetector::findMIPSABIDirSuffix(
// /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);
+
+ // 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);
- if (isMips16(Args))
- Suffix += "/mips16";
- } else {
- if (isMips64r2(Args))
- Suffix += hasMipsABIArg(Args, "n32") ? "/mips64r2" : "/mips64r2/64";
- else
- Suffix += hasMipsABIArg(Args, "n32") ? "/mips64" : "/mips64/64";
- }
+ llvm::Triple::ArchType TargetArch = TargetTriple.getArch();
- if (TargetArch == llvm::Triple::mipsel ||
- TargetArch == llvm::Triple::mips64el)
- Suffix += "/el";
+ 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) {
- if (isSoftFloatABI(Args))
- Suffix += "/sof";
+ // 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 (isMipsFP64(Args))
- Suffix += "/fp64";
-
- if (isMipsNan2008(Args))
- Suffix += "/nan2008";
+ if (NeedsBiarchSuffix)
+ DefaultIs32Bit = TargetTriple.isArch64Bit();
+ else
+ DefaultIs32Bit = TargetTriple.isArch32Bit();
}
- if (hasCrtBeginObj(Path + Suffix))
- return;
+ if (DefaultIs32Bit)
+ Default.flag("+m32").flag("-m64");
+ else
+ Default.flag("-m32").flag("+m64");
- // Check Code Sourcery Toolchain path
- Suffix.clear();
- if (isMips16(Args))
- Suffix += "/mips16";
- else if (isMicroMips(Args))
- Suffix += "/micromips";
+ Multilibs.push_back(Default);
+ Multilibs.push_back(Alt64);
+ Multilibs.push_back(Alt32);
- if (isSoftFloatABI(Args))
- Suffix += "/soft-float";
- else if (isMipsNan2008(Args))
- Suffix += "/nan2008";
+ Multilibs.FilterOut(NonExistant);
- if (TargetArch == llvm::Triple::mipsel ||
- TargetArch == llvm::Triple::mips64el)
- Suffix += "/el";
+ Multilib::flags_list Flags;
+ addMultilibFlag(TargetTriple.isArch64Bit(), "m64", Flags);
+ addMultilibFlag(TargetTriple.isArch32Bit(), "m32", Flags);
- if (hasCrtBeginObj(Path + Suffix))
- return;
+ if (!Multilibs.select(Flags, SelectedMultilib))
+ return false;
- Suffix.clear();
+ if (SelectedMultilib == Alt64 || SelectedMultilib == Alt32) {
+ BiarchSibling = Default;
+ }
+
+ 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::GCCInstallationDetector::ScanLibDirForGCCTriple(
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::GCCInstallationDetector::ScanLibDirForGCCTriple(
// 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 llvm::Triple& Triple, const ArgList &Args)
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, ToolChain::path_list &Paths) {
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 llvm::Triple &Triple,
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::Triple &Triple, const ArgList &Args)
// 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::Triple &Triple, const ArgList &Args)
//
// 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::Triple &Triple, const ArgList &Args)
// 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() const {
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(const ArgList &DriverArgs,
/// 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(const ArgList &DriverArgs,
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(const ArgList &DriverArgs,
for (unsigned i = 0; i < llvm::array_lengthof(LibStdCXXIncludePathCandidates);
++i) {
if (addLibStdCXXIncludePaths(LibStdCXXIncludePathCandidates[i],
- TripleStr + MIPSABIDirSuffix + BiarchSuffix,
+ TripleStr + Multilib.includeSuffix(),
DriverArgs, CC1Args))
break;
}
diff --git a/lib/Driver/ToolChains.h b/lib/Driver/ToolChains.h
index d5d058161f..6c3b084bf7 100644
--- a/lib/Driver/ToolChains.h
+++ b/lib/Driver/ToolChains.h
@@ -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,
diff --git a/lib/Driver/Tools.cpp b/lib/Driver/Tools.cpp
index b3a2b86544..5087ce6816 100644
--- a/lib/Driver/Tools.cpp
+++ b/lib/Driver/Tools.cpp
@@ -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 llvm::Triple &Triple) {
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 ArgList &Args,
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 ArgList &Args,
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 ArgList &Args,
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, const JobAction &JA,
// -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, const JobAction &JA,
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(const ArgList &args,
// -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(Compilation &C, const JobAction &JA,
// 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(StringRef CPU) {
.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(Compilation &C, const JobAction &JA,
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(const ArgList &Args,
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(Compilation &C, const JobAction &JA,
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");
diff --git a/lib/Driver/Tools.h b/lib/Driver/Tools.h
index 873969b4d7..4fe42b87cd 100644
--- a/lib/Driver/Tools.h
+++ b/lib/Driver/Tools.h
@@ -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);
diff --git a/unittests/CMakeLists.txt b/unittests/CMakeLists.txt
index 479b36f84c..f00008e040 100644
--- a/unittests/CMakeLists.txt
+++ b/unittests/CMakeLists.txt
@@ -11,6 +11,7 @@ endfunction()
add_subdirectory(Basic)
add_subdirectory(Lex)
+add_subdirectory(Driver)
if(CLANG_ENABLE_STATIC_ANALYZER)
add_subdirectory(Frontend)
endif()
diff --git a/unittests/Driver/CMakeLists.txt b/unittests/Driver/CMakeLists.txt
new file mode 100644
index 0000000000..154197a856
--- /dev/null
+++ b/unittests/Driver/CMakeLists.txt
@@ -0,0 +1,11 @@
+set(LLVM_LINK_COMPONENTS
+ Support
+ )
+
+add_clang_unittest(DriverTests
+ MultilibTest.cpp
+ )
+
+target_link_libraries(DriverTests
+ clangDriver
+ )
diff --git a/unittests/Driver/Makefile b/unittests/Driver/Makefile
new file mode 100644
index 0000000000..21d19f34f6
--- /dev/null
+++ b/unittests/Driver/Makefile
@@ -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
diff --git a/unittests/Driver/MultilibTest.cpp b/unittests/Driver/MultilibTest.cpp
new file mode 100644
index 0000000000..c036290c02
--- /dev/null
+++ b/unittests/Driver/MultilibTest.cpp
@@ -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);
+}
diff --git a/unittests/Makefile b/unittests/Makefile
index d0dfe471d1..e4fbe584da 100644
--- a/unittests/Makefile
+++ b/unittests/Makefile
@@ -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