michaelplatings updated this revision to Diff 495798.
michaelplatings added a comment.

Replace makeMultilibBuilder() with a MultilibBuilder constructor that 
initializes all suffixes to the same value.

In the case of AndroidMipsMultilibs it was apparently intended that only the 
GCC suffix was set, and the other suffixes were left empty. This is now 
explicit in the code.

Comments about Multilib being "immutable" were overstated so I've now removed 
them. The intention is merely to remove the requirement to support mutation. 
The code is simpler when you don't need to keep checking invariants.


Repository:
  rG LLVM Github Monorepo

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

https://reviews.llvm.org/D142893

Files:
  clang/include/clang/Driver/Multilib.h
  clang/include/clang/Driver/MultilibBuilder.h
  clang/lib/Driver/CMakeLists.txt
  clang/lib/Driver/Multilib.cpp
  clang/lib/Driver/MultilibBuilder.cpp
  clang/lib/Driver/ToolChains/BareMetal.cpp
  clang/lib/Driver/ToolChains/Fuchsia.cpp
  clang/lib/Driver/ToolChains/Gnu.cpp
  clang/unittests/Driver/CMakeLists.txt
  clang/unittests/Driver/MultilibBuilderTest.cpp
  clang/unittests/Driver/MultilibTest.cpp

Index: clang/unittests/Driver/MultilibTest.cpp
===================================================================
--- clang/unittests/Driver/MultilibTest.cpp
+++ clang/unittests/Driver/MultilibTest.cpp
@@ -11,34 +11,17 @@
 //===----------------------------------------------------------------------===//
 
 #include "clang/Driver/Multilib.h"
+#include "../../lib/Driver/ToolChains/CommonArgs.h"
 #include "clang/Basic/LLVM.h"
+#include "llvm/ADT/ArrayRef.h"
 #include "llvm/ADT/StringRef.h"
 #include "llvm/ADT/StringSwitch.h"
+#include "llvm/Support/SourceMgr.h"
 #include "gtest/gtest.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";
@@ -50,40 +33,28 @@
 }
 
 TEST(MultilibTest, OpEqReflexivity3) {
-  Multilib M1, M2;
-  M1.flag("+foo");
-  M2.flag("+foo");
+  Multilib M1({}, {}, {}, 0, {"+foo"});
+  Multilib M2({}, {}, {}, 0, {"+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");
+  Multilib M1({}, {}, {}, 0, {"+foo"});
+  Multilib M2({}, {}, {}, 0, {"-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");
+  Multilib M1;
+  Multilib M2({}, {}, {}, 0, {"+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");
+  Multilib M1("/64");
+  Multilib M2("/64");
   ASSERT_TRUE(M1 == M2)
       << "Constructor argument must match Multilib::gccSuffix()";
   ASSERT_TRUE(M2 == M1)
@@ -91,9 +62,8 @@
 }
 
 TEST(MultilibTest, OpEqEquivalence3) {
-  Multilib M1("", "32");
-  Multilib M2;
-  M2.osSuffix("/32");
+  Multilib M1("", "/32");
+  Multilib M2("", "/32");
   ASSERT_TRUE(M1 == M2)
       << "Constructor argument must match Multilib::osSuffix()";
   ASSERT_TRUE(M2 == M1)
@@ -101,9 +71,8 @@
 }
 
 TEST(MultilibTest, OpEqEquivalence4) {
-  Multilib M1("", "", "16");
-  Multilib M2;
-  M2.includeSuffix("/16");
+  Multilib M1("", "", "/16");
+  Multilib M2("", "", "/16");
   ASSERT_TRUE(M1 == M2)
       << "Constructor argument must match Multilib::includeSuffix()";
   ASSERT_TRUE(M2 == M1)
@@ -111,31 +80,31 @@
 }
 
 TEST(MultilibTest, OpEqInequivalence3) {
-  Multilib M1("foo");
-  Multilib M2("bar");
+  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");
+  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");
+  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");
+  Multilib M("/gcc64", "/os64", "/inc64");
   ASSERT_TRUE(M.gccSuffix() == "/gcc64");
   ASSERT_TRUE(M.osSuffix() == "/os64");
   ASSERT_TRUE(M.includeSuffix() == "/inc64");
@@ -155,7 +124,7 @@
 }
 
 TEST(MultilibTest, Construction3) {
-  Multilib M = Multilib().flag("+f1").flag("+f2").flag("-f3");
+  Multilib M({}, {}, {}, 0, {"+f1", "+f2", "-f3"});
   for (Multilib::flags_list::const_iterator I = M.flags().begin(),
                                             E = M.flags().end();
        I != E; ++I) {
@@ -165,208 +134,32 @@
   }
 }
 
-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"));
+  MultilibSet MS({
+      Multilib("/one"),
+      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) {
-  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([](const Multilib &M) {
-    return StringRef(M.gccSuffix()).startswith("/p");
-  });
-  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);
 }
 
 TEST(MultilibTest, SetPriority) {
-  MultilibSet MS;
-  MS.push_back(Multilib("foo", {}, {}, 1).flag("+foo"));
-  MS.push_back(Multilib("bar", {}, {}, 2).flag("+bar"));
-
-  Multilib::flags_list Flags1;
-  Flags1.push_back("+foo");
-  Flags1.push_back("-bar");
+  MultilibSet MS({
+      Multilib("/foo", {}, {}, 1, {"+foo"}),
+      Multilib("/bar", {}, {}, 2, {"+bar"}),
+  });
+  Multilib::flags_list Flags1 = {"+foo", "-bar"};
   Multilib Selection1;
   ASSERT_TRUE(MS.select(Flags1, Selection1))
       << "Flag set was {\"+foo\"}, but selection not found";
   ASSERT_TRUE(Selection1.gccSuffix() == "/foo")
       << "Selection picked " << Selection1 << " which was not expected";
 
-  Multilib::flags_list Flags2;
-  Flags2.push_back("+foo");
-  Flags2.push_back("+bar");
+  Multilib::flags_list Flags2 = {"+foo", "+bar"};
   Multilib Selection2;
   ASSERT_TRUE(MS.select(Flags2, Selection2))
       << "Flag set was {\"+bar\"}, but selection not found";
Index: clang/unittests/Driver/MultilibBuilderTest.cpp
===================================================================
--- /dev/null
+++ clang/unittests/Driver/MultilibBuilderTest.cpp
@@ -0,0 +1,209 @@
+//===- unittests/Driver/MultilibBuilderTest.cpp --- MultilibBuilder tests
+//---------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// Unit tests for MultilibBuilder and MultilibSetBuilder
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang/Driver/MultilibBuilder.h"
+#include "../../lib/Driver/ToolChains/CommonArgs.h"
+#include "clang/Basic/LLVM.h"
+#include "llvm/ADT/ArrayRef.h"
+#include "llvm/ADT/StringRef.h"
+#include "llvm/ADT/StringSwitch.h"
+#include "gtest/gtest.h"
+
+using llvm::is_contained;
+using namespace clang;
+using namespace driver;
+
+TEST(MultilibBuilderTest, MultilibValidity) {
+
+  ASSERT_TRUE(MultilibBuilder().isValid()) << "Empty multilib is not valid";
+
+  ASSERT_TRUE(MultilibBuilder().flag("+foo").isValid())
+      << "Single indicative flag is not valid";
+
+  ASSERT_TRUE(MultilibBuilder().flag("-foo").isValid())
+      << "Single contraindicative flag is not valid";
+
+  ASSERT_FALSE(MultilibBuilder().flag("+foo").flag("-foo").isValid())
+      << "Conflicting flags should invalidate the Multilib";
+
+  ASSERT_TRUE(MultilibBuilder().flag("+foo").flag("+foo").isValid())
+      << "Multilib should be valid even if it has the same flag "
+         "twice";
+
+  ASSERT_TRUE(MultilibBuilder().flag("+foo").flag("-foobar").isValid())
+      << "Seemingly conflicting prefixes shouldn't actually conflict";
+}
+
+TEST(MultilibBuilderTest, Construction1) {
+  MultilibBuilder M("gcc64", "os64", "inc64");
+  ASSERT_TRUE(M.gccSuffix() == "/gcc64");
+  ASSERT_TRUE(M.osSuffix() == "/os64");
+  ASSERT_TRUE(M.includeSuffix() == "/inc64");
+}
+
+TEST(MultilibBuilderTest, Construction3) {
+  MultilibBuilder M = MultilibBuilder().flag("+f1").flag("+f2").flag("-f3");
+  for (const std::string &A : M.flags()) {
+    ASSERT_TRUE(llvm::StringSwitch<bool>(A)
+                    .Cases("+f1", "+f2", "-f3", true)
+                    .Default(false));
+  }
+}
+
+TEST(MultilibBuilderTest, SetConstruction1) {
+  // Single maybe
+  MultilibSet MS = MultilibSetBuilder()
+                       .Maybe(MultilibBuilder("64").flag("+m64"))
+                       .makeMultilibSet();
+  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().begin() == "+m64");
+    else if (I->gccSuffix() == "")
+      ASSERT_TRUE(*I->flags().begin() == "-m64");
+    else
+      FAIL() << "Unrecognized gccSufix: " << I->gccSuffix();
+  }
+}
+
+TEST(MultilibBuilderTest, SetConstruction2) {
+  // Double maybe
+  MultilibSet MS = MultilibSetBuilder()
+                       .Maybe(MultilibBuilder("sof").flag("+sof"))
+                       .Maybe(MultilibBuilder("el").flag("+EL"))
+                       .makeMultilibSet();
+  ASSERT_TRUE(MS.size() == 4);
+  for (MultilibSet::const_iterator I = MS.begin(), E = MS.end(); I != E; ++I) {
+    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("", is_contained(I->flags(), "-sof"))
+                    .Case("/sof", is_contained(I->flags(), "+sof"))
+                    .Case("/el", is_contained(I->flags(), "-sof"))
+                    .Case("/sof/el", is_contained(I->flags(), "+sof"))
+                    .Default(false))
+        << "Multilib " << *I << " didn't have the appropriate {+,-}sof flag";
+    ASSERT_TRUE(llvm::StringSwitch<bool>(I->gccSuffix())
+                    .Case("", is_contained(I->flags(), "-EL"))
+                    .Case("/sof", is_contained(I->flags(), "-EL"))
+                    .Case("/el", is_contained(I->flags(), "+EL"))
+                    .Case("/sof/el", is_contained(I->flags(), "+EL"))
+                    .Default(false))
+        << "Multilib " << *I << " didn't have the appropriate {+,-}EL flag";
+  }
+}
+
+TEST(MultilibBuilderTest, SetRegexFilter) {
+  MultilibSetBuilder MB;
+  MB.Maybe(MultilibBuilder("one"))
+      .Maybe(MultilibBuilder("two"))
+      .Maybe(MultilibBuilder("three"))
+      .makeMultilibSet();
+  MultilibSet MS = MB.makeMultilibSet();
+  ASSERT_EQ(MS.size(), (unsigned)2 * 2 * 2)
+      << "Size before filter was incorrect. Contents:\n"
+      << MS;
+  MB.FilterOut("/one/two/three");
+  MS = MB.makeMultilibSet();
+  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(MultilibBuilderTest, SetFilterObject) {
+  MultilibSet MS = MultilibSetBuilder()
+                       .Maybe(MultilibBuilder("orange"))
+                       .Maybe(MultilibBuilder("pear"))
+                       .Maybe(MultilibBuilder("plum"))
+                       .makeMultilibSet();
+  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([](const Multilib &M) {
+    return StringRef(M.gccSuffix()).startswith("/p");
+  });
+  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(MultilibBuilderTest, SetSelection1) {
+  MultilibSet MS1 = MultilibSetBuilder()
+                        .Maybe(MultilibBuilder("64").flag("+m64"))
+                        .makeMultilibSet();
+
+  Multilib::flags_list FlagM64 = {"+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 = {"-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(MultilibBuilderTest, SetSelection2) {
+  MultilibSet MS2 = MultilibSetBuilder()
+                        .Maybe(MultilibBuilder("el").flag("+EL"))
+                        .Maybe(MultilibBuilder("sf").flag("+SF"))
+                        .makeMultilibSet();
+
+  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 ";
+  }
+}
Index: clang/unittests/Driver/CMakeLists.txt
===================================================================
--- clang/unittests/Driver/CMakeLists.txt
+++ clang/unittests/Driver/CMakeLists.txt
@@ -11,6 +11,7 @@
   DXCModeTest.cpp
   ToolChainTest.cpp
   ModuleCacheTest.cpp
+  MultilibBuilderTest.cpp
   MultilibTest.cpp
   SanitizerArgsTest.cpp
   )
Index: clang/lib/Driver/ToolChains/Gnu.cpp
===================================================================
--- clang/lib/Driver/ToolChains/Gnu.cpp
+++ clang/lib/Driver/ToolChains/Gnu.cpp
@@ -20,6 +20,7 @@
 #include "clang/Driver/Compilation.h"
 #include "clang/Driver/Driver.h"
 #include "clang/Driver/DriverDiagnostic.h"
+#include "clang/Driver/MultilibBuilder.h"
 #include "clang/Driver/Options.h"
 #include "clang/Driver/Tool.h"
 #include "clang/Driver/ToolChain.h"
@@ -1045,38 +1046,34 @@
   return Arch == llvm::Triple::msp430;
 }
 
-static Multilib makeMultilib(StringRef commonSuffix) {
-  return Multilib(commonSuffix, commonSuffix, commonSuffix);
-}
-
 static bool findMipsCsMultilibs(const Multilib::flags_list &Flags,
                                 FilterNonExistent &NonExistent,
                                 DetectedMultilibs &Result) {
   // Check for Code Sourcery toolchain multilibs
   MultilibSet CSMipsMultilibs;
   {
-    auto MArchMips16 = makeMultilib("/mips16").flag("+m32").flag("+mips16");
+    auto MArchMips16 = MultilibBuilder("/mips16").flag("+m32").flag("+mips16");
 
     auto MArchMicroMips =
-        makeMultilib("/micromips").flag("+m32").flag("+mmicromips");
+        MultilibBuilder("/micromips").flag("+m32").flag("+mmicromips");
 
-    auto MArchDefault = makeMultilib("").flag("-mips16").flag("-mmicromips");
+    auto MArchDefault = MultilibBuilder("").flag("-mips16").flag("-mmicromips");
 
-    auto UCLibc = makeMultilib("/uclibc").flag("+muclibc");
+    auto UCLibc = MultilibBuilder("/uclibc").flag("+muclibc");
 
-    auto SoftFloat = makeMultilib("/soft-float").flag("+msoft-float");
+    auto SoftFloat = MultilibBuilder("/soft-float").flag("+msoft-float");
 
-    auto Nan2008 = makeMultilib("/nan2008").flag("+mnan=2008");
+    auto Nan2008 = MultilibBuilder("/nan2008").flag("+mnan=2008");
 
     auto DefaultFloat =
-        makeMultilib("").flag("-msoft-float").flag("-mnan=2008");
+        MultilibBuilder("").flag("-msoft-float").flag("-mnan=2008");
 
-    auto BigEndian = makeMultilib("").flag("+EB").flag("-EL");
+    auto BigEndian = MultilibBuilder("").flag("+EB").flag("-EL");
 
-    auto LittleEndian = makeMultilib("/el").flag("+EL").flag("-EB");
+    auto LittleEndian = MultilibBuilder("/el").flag("+EL").flag("-EB");
 
     // Note that this one's osSuffix is ""
-    auto MAbi64 = makeMultilib("")
+    auto MAbi64 = MultilibBuilder("")
                       .gccSuffix("/64")
                       .includeSuffix("/64")
                       .flag("+mabi=n64")
@@ -1084,7 +1081,7 @@
                       .flag("-m32");
 
     CSMipsMultilibs =
-        MultilibSet()
+        MultilibSetBuilder()
             .Either(MArchMips16, MArchMicroMips, MArchDefault)
             .Maybe(UCLibc)
             .Either(SoftFloat, Nan2008, DefaultFloat)
@@ -1094,6 +1091,7 @@
             .Maybe(MAbi64)
             .FilterOut("/mips16.*/64")
             .FilterOut("/micromips.*/64")
+            .makeMultilibSet()
             .FilterOut(NonExistent)
             .setIncludeDirsCallback([](const Multilib &M) {
               std::vector<std::string> Dirs({"/include"});
@@ -1108,21 +1106,25 @@
 
   MultilibSet DebianMipsMultilibs;
   {
-    Multilib MAbiN32 =
-        Multilib().gccSuffix("/n32").includeSuffix("/n32").flag("+mabi=n32");
+    MultilibBuilder MAbiN32 =
+        MultilibBuilder().gccSuffix("/n32").includeSuffix("/n32").flag(
+            "+mabi=n32");
 
-    Multilib M64 = Multilib()
-                       .gccSuffix("/64")
-                       .includeSuffix("/64")
-                       .flag("+m64")
-                       .flag("-m32")
-                       .flag("-mabi=n32");
+    MultilibBuilder M64 = MultilibBuilder()
+                              .gccSuffix("/64")
+                              .includeSuffix("/64")
+                              .flag("+m64")
+                              .flag("-m32")
+                              .flag("-mabi=n32");
 
-    Multilib M32 =
-        Multilib().gccSuffix("/32").flag("-m64").flag("+m32").flag("-mabi=n32");
+    MultilibBuilder M32 =
+        MultilibBuilder().gccSuffix("/32").flag("-m64").flag("+m32").flag(
+            "-mabi=n32");
 
-    DebianMipsMultilibs =
-        MultilibSet().Either(M32, M64, MAbiN32).FilterOut(NonExistent);
+    DebianMipsMultilibs = MultilibSetBuilder()
+                              .Either(M32, M64, MAbiN32)
+                              .makeMultilibSet()
+                              .FilterOut(NonExistent);
   }
 
   // Sort candidates. Toolchain that best meets the directories tree goes first.
@@ -1147,25 +1149,32 @@
                                      DetectedMultilibs &Result) {
 
   MultilibSet AndroidMipsMultilibs =
-      MultilibSet()
-          .Maybe(Multilib("/mips-r2").flag("+march=mips32r2"))
-          .Maybe(Multilib("/mips-r6").flag("+march=mips32r6"))
+      MultilibSetBuilder()
+          .Maybe(MultilibBuilder("/mips-r2", {}, {}).flag("+march=mips32r2"))
+          .Maybe(MultilibBuilder("/mips-r6", {}, {}).flag("+march=mips32r6"))
+          .makeMultilibSet()
           .FilterOut(NonExistent);
 
   MultilibSet AndroidMipselMultilibs =
-      MultilibSet()
-          .Either(Multilib().flag("+march=mips32"),
-                  Multilib("/mips-r2", "", "/mips-r2").flag("+march=mips32r2"),
-                  Multilib("/mips-r6", "", "/mips-r6").flag("+march=mips32r6"))
+      MultilibSetBuilder()
+          .Either(MultilibBuilder().flag("+march=mips32"),
+                  MultilibBuilder("/mips-r2", "", "/mips-r2")
+                      .flag("+march=mips32r2"),
+                  MultilibBuilder("/mips-r6", "", "/mips-r6")
+                      .flag("+march=mips32r6"))
+          .makeMultilibSet()
           .FilterOut(NonExistent);
 
   MultilibSet AndroidMips64elMultilibs =
-      MultilibSet()
-          .Either(
-              Multilib().flag("+march=mips64r6"),
-              Multilib("/32/mips-r1", "", "/mips-r1").flag("+march=mips32"),
-              Multilib("/32/mips-r2", "", "/mips-r2").flag("+march=mips32r2"),
-              Multilib("/32/mips-r6", "", "/mips-r6").flag("+march=mips32r6"))
+      MultilibSetBuilder()
+          .Either(MultilibBuilder().flag("+march=mips64r6"),
+                  MultilibBuilder("/32/mips-r1", "", "/mips-r1")
+                      .flag("+march=mips32"),
+                  MultilibBuilder("/32/mips-r2", "", "/mips-r2")
+                      .flag("+march=mips32r2"),
+                  MultilibBuilder("/32/mips-r6", "", "/mips-r6")
+                      .flag("+march=mips32r6"))
+          .makeMultilibSet()
           .FilterOut(NonExistent);
 
   MultilibSet *MS = &AndroidMipsMultilibs;
@@ -1186,18 +1195,20 @@
   // Musl toolchain multilibs
   MultilibSet MuslMipsMultilibs;
   {
-    auto MArchMipsR2 = makeMultilib("")
+    auto MArchMipsR2 = MultilibBuilder("")
                            .osSuffix("/mips-r2-hard-musl")
                            .flag("+EB")
                            .flag("-EL")
                            .flag("+march=mips32r2");
 
-    auto MArchMipselR2 = makeMultilib("/mipsel-r2-hard-musl")
+    auto MArchMipselR2 = MultilibBuilder("/mipsel-r2-hard-musl")
                              .flag("-EB")
                              .flag("+EL")
                              .flag("+march=mips32r2");
 
-    MuslMipsMultilibs = MultilibSet().Either(MArchMipsR2, MArchMipselR2);
+    MuslMipsMultilibs = MultilibSetBuilder()
+                            .Either(MArchMipsR2, MArchMipselR2)
+                            .makeMultilibSet();
 
     // Specify the callback that computes the include directories.
     MuslMipsMultilibs.setIncludeDirsCallback([](const Multilib &M) {
@@ -1218,48 +1229,49 @@
   // CodeScape MTI toolchain v1.2 and early.
   MultilibSet MtiMipsMultilibsV1;
   {
-    auto MArchMips32 = makeMultilib("/mips32")
+    auto MArchMips32 = MultilibBuilder("/mips32")
                            .flag("+m32")
                            .flag("-m64")
                            .flag("-mmicromips")
                            .flag("+march=mips32");
 
-    auto MArchMicroMips = makeMultilib("/micromips")
+    auto MArchMicroMips = MultilibBuilder("/micromips")
                               .flag("+m32")
                               .flag("-m64")
                               .flag("+mmicromips");
 
-    auto MArchMips64r2 = makeMultilib("/mips64r2")
+    auto MArchMips64r2 = MultilibBuilder("/mips64r2")
                              .flag("-m32")
                              .flag("+m64")
                              .flag("+march=mips64r2");
 
-    auto MArchMips64 = makeMultilib("/mips64").flag("-m32").flag("+m64").flag(
-        "-march=mips64r2");
+    auto MArchMips64 =
+        MultilibBuilder("/mips64").flag("-m32").flag("+m64").flag(
+            "-march=mips64r2");
 
-    auto MArchDefault = makeMultilib("")
+    auto MArchDefault = MultilibBuilder("")
                             .flag("+m32")
                             .flag("-m64")
                             .flag("-mmicromips")
                             .flag("+march=mips32r2");
 
-    auto Mips16 = makeMultilib("/mips16").flag("+mips16");
+    auto Mips16 = MultilibBuilder("/mips16").flag("+mips16");
 
-    auto UCLibc = makeMultilib("/uclibc").flag("+muclibc");
+    auto UCLibc = MultilibBuilder("/uclibc").flag("+muclibc");
 
     auto MAbi64 =
-        makeMultilib("/64").flag("+mabi=n64").flag("-mabi=n32").flag("-m32");
+        MultilibBuilder("/64").flag("+mabi=n64").flag("-mabi=n32").flag("-m32");
 
-    auto BigEndian = makeMultilib("").flag("+EB").flag("-EL");
+    auto BigEndian = MultilibBuilder("").flag("+EB").flag("-EL");
 
-    auto LittleEndian = makeMultilib("/el").flag("+EL").flag("-EB");
+    auto LittleEndian = MultilibBuilder("/el").flag("+EL").flag("-EB");
 
-    auto SoftFloat = makeMultilib("/sof").flag("+msoft-float");
+    auto SoftFloat = MultilibBuilder("/sof").flag("+msoft-float");
 
-    auto Nan2008 = makeMultilib("/nan2008").flag("+mnan=2008");
+    auto Nan2008 = MultilibBuilder("/nan2008").flag("+mnan=2008");
 
     MtiMipsMultilibsV1 =
-        MultilibSet()
+        MultilibSetBuilder()
             .Either(MArchMips32, MArchMicroMips, MArchMips64r2, MArchMips64,
                     MArchDefault)
             .Maybe(UCLibc)
@@ -1276,6 +1288,7 @@
             .Maybe(SoftFloat)
             .Maybe(Nan2008)
             .FilterOut(".*sof/nan2008")
+            .makeMultilibSet()
             .FilterOut(NonExistent)
             .setIncludeDirsCallback([](const Multilib &M) {
               std::vector<std::string> Dirs({"/include"});
@@ -1290,80 +1303,87 @@
   // CodeScape IMG toolchain starting from v1.3.
   MultilibSet MtiMipsMultilibsV2;
   {
-    auto BeHard = makeMultilib("/mips-r2-hard")
+    auto BeHard = MultilibBuilder("/mips-r2-hard")
                       .flag("+EB")
                       .flag("-msoft-float")
                       .flag("-mnan=2008")
                       .flag("-muclibc");
-    auto BeSoft = makeMultilib("/mips-r2-soft")
+    auto BeSoft = MultilibBuilder("/mips-r2-soft")
                       .flag("+EB")
                       .flag("+msoft-float")
                       .flag("-mnan=2008");
-    auto ElHard = makeMultilib("/mipsel-r2-hard")
+    auto ElHard = MultilibBuilder("/mipsel-r2-hard")
                       .flag("+EL")
                       .flag("-msoft-float")
                       .flag("-mnan=2008")
                       .flag("-muclibc");
-    auto ElSoft = makeMultilib("/mipsel-r2-soft")
+    auto ElSoft = MultilibBuilder("/mipsel-r2-soft")
                       .flag("+EL")
                       .flag("+msoft-float")
                       .flag("-mnan=2008")
                       .flag("-mmicromips");
-    auto BeHardNan = makeMultilib("/mips-r2-hard-nan2008")
+    auto BeHardNan = MultilibBuilder("/mips-r2-hard-nan2008")
                          .flag("+EB")
                          .flag("-msoft-float")
                          .flag("+mnan=2008")
                          .flag("-muclibc");
-    auto ElHardNan = makeMultilib("/mipsel-r2-hard-nan2008")
+    auto ElHardNan = MultilibBuilder("/mipsel-r2-hard-nan2008")
                          .flag("+EL")
                          .flag("-msoft-float")
                          .flag("+mnan=2008")
                          .flag("-muclibc")
                          .flag("-mmicromips");
-    auto BeHardNanUclibc = makeMultilib("/mips-r2-hard-nan2008-uclibc")
+    auto BeHardNanUclibc = MultilibBuilder("/mips-r2-hard-nan2008-uclibc")
                                .flag("+EB")
                                .flag("-msoft-float")
                                .flag("+mnan=2008")
                                .flag("+muclibc");
-    auto ElHardNanUclibc = makeMultilib("/mipsel-r2-hard-nan2008-uclibc")
+    auto ElHardNanUclibc = MultilibBuilder("/mipsel-r2-hard-nan2008-uclibc")
                                .flag("+EL")
                                .flag("-msoft-float")
                                .flag("+mnan=2008")
                                .flag("+muclibc");
-    auto BeHardUclibc = makeMultilib("/mips-r2-hard-uclibc")
+    auto BeHardUclibc = MultilibBuilder("/mips-r2-hard-uclibc")
                             .flag("+EB")
                             .flag("-msoft-float")
                             .flag("-mnan=2008")
                             .flag("+muclibc");
-    auto ElHardUclibc = makeMultilib("/mipsel-r2-hard-uclibc")
+    auto ElHardUclibc = MultilibBuilder("/mipsel-r2-hard-uclibc")
                             .flag("+EL")
                             .flag("-msoft-float")
                             .flag("-mnan=2008")
                             .flag("+muclibc");
-    auto ElMicroHardNan = makeMultilib("/micromipsel-r2-hard-nan2008")
+    auto ElMicroHardNan = MultilibBuilder("/micromipsel-r2-hard-nan2008")
                               .flag("+EL")
                               .flag("-msoft-float")
                               .flag("+mnan=2008")
                               .flag("+mmicromips");
-    auto ElMicroSoft = makeMultilib("/micromipsel-r2-soft")
+    auto ElMicroSoft = MultilibBuilder("/micromipsel-r2-soft")
                            .flag("+EL")
                            .flag("+msoft-float")
                            .flag("-mnan=2008")
                            .flag("+mmicromips");
 
-    auto O32 =
-        makeMultilib("/lib").osSuffix("").flag("-mabi=n32").flag("-mabi=n64");
-    auto N32 =
-        makeMultilib("/lib32").osSuffix("").flag("+mabi=n32").flag("-mabi=n64");
-    auto N64 =
-        makeMultilib("/lib64").osSuffix("").flag("-mabi=n32").flag("+mabi=n64");
+    auto O32 = MultilibBuilder("/lib")
+                   .osSuffix("")
+                   .flag("-mabi=n32")
+                   .flag("-mabi=n64");
+    auto N32 = MultilibBuilder("/lib32")
+                   .osSuffix("")
+                   .flag("+mabi=n32")
+                   .flag("-mabi=n64");
+    auto N64 = MultilibBuilder("/lib64")
+                   .osSuffix("")
+                   .flag("-mabi=n32")
+                   .flag("+mabi=n64");
 
     MtiMipsMultilibsV2 =
-        MultilibSet()
+        MultilibSetBuilder()
             .Either({BeHard, BeSoft, ElHard, ElSoft, BeHardNan, ElHardNan,
                      BeHardNanUclibc, ElHardNanUclibc, BeHardUclibc,
                      ElHardUclibc, ElMicroHardNan, ElMicroSoft})
             .Either(O32, N32, N64)
+            .makeMultilibSet()
             .FilterOut(NonExistent)
             .setIncludeDirsCallback([](const Multilib &M) {
               return std::vector<std::string>({"/../../../../sysroot" +
@@ -1390,18 +1410,19 @@
   // CodeScape IMG toolchain v1.2 and early.
   MultilibSet ImgMultilibsV1;
   {
-    auto Mips64r6 = makeMultilib("/mips64r6").flag("+m64").flag("-m32");
+    auto Mips64r6 = MultilibBuilder("/mips64r6").flag("+m64").flag("-m32");
 
-    auto LittleEndian = makeMultilib("/el").flag("+EL").flag("-EB");
+    auto LittleEndian = MultilibBuilder("/el").flag("+EL").flag("-EB");
 
     auto MAbi64 =
-        makeMultilib("/64").flag("+mabi=n64").flag("-mabi=n32").flag("-m32");
+        MultilibBuilder("/64").flag("+mabi=n64").flag("-mabi=n32").flag("-m32");
 
     ImgMultilibsV1 =
-        MultilibSet()
+        MultilibSetBuilder()
             .Maybe(Mips64r6)
             .Maybe(MAbi64)
             .Maybe(LittleEndian)
+            .makeMultilibSet()
             .FilterOut(NonExistent)
             .setIncludeDirsCallback([](const Multilib &M) {
               return std::vector<std::string>(
@@ -1412,51 +1433,58 @@
   // CodeScape IMG toolchain starting from v1.3.
   MultilibSet ImgMultilibsV2;
   {
-    auto BeHard = makeMultilib("/mips-r6-hard")
+    auto BeHard = MultilibBuilder("/mips-r6-hard")
                       .flag("+EB")
                       .flag("-msoft-float")
                       .flag("-mmicromips");
-    auto BeSoft = makeMultilib("/mips-r6-soft")
+    auto BeSoft = MultilibBuilder("/mips-r6-soft")
                       .flag("+EB")
                       .flag("+msoft-float")
                       .flag("-mmicromips");
-    auto ElHard = makeMultilib("/mipsel-r6-hard")
+    auto ElHard = MultilibBuilder("/mipsel-r6-hard")
                       .flag("+EL")
                       .flag("-msoft-float")
                       .flag("-mmicromips");
-    auto ElSoft = makeMultilib("/mipsel-r6-soft")
+    auto ElSoft = MultilibBuilder("/mipsel-r6-soft")
                       .flag("+EL")
                       .flag("+msoft-float")
                       .flag("-mmicromips");
-    auto BeMicroHard = makeMultilib("/micromips-r6-hard")
+    auto BeMicroHard = MultilibBuilder("/micromips-r6-hard")
                            .flag("+EB")
                            .flag("-msoft-float")
                            .flag("+mmicromips");
-    auto BeMicroSoft = makeMultilib("/micromips-r6-soft")
+    auto BeMicroSoft = MultilibBuilder("/micromips-r6-soft")
                            .flag("+EB")
                            .flag("+msoft-float")
                            .flag("+mmicromips");
-    auto ElMicroHard = makeMultilib("/micromipsel-r6-hard")
+    auto ElMicroHard = MultilibBuilder("/micromipsel-r6-hard")
                            .flag("+EL")
                            .flag("-msoft-float")
                            .flag("+mmicromips");
-    auto ElMicroSoft = makeMultilib("/micromipsel-r6-soft")
+    auto ElMicroSoft = MultilibBuilder("/micromipsel-r6-soft")
                            .flag("+EL")
                            .flag("+msoft-float")
                            .flag("+mmicromips");
 
-    auto O32 =
-        makeMultilib("/lib").osSuffix("").flag("-mabi=n32").flag("-mabi=n64");
-    auto N32 =
-        makeMultilib("/lib32").osSuffix("").flag("+mabi=n32").flag("-mabi=n64");
-    auto N64 =
-        makeMultilib("/lib64").osSuffix("").flag("-mabi=n32").flag("+mabi=n64");
+    auto O32 = MultilibBuilder("/lib")
+                   .osSuffix("")
+                   .flag("-mabi=n32")
+                   .flag("-mabi=n64");
+    auto N32 = MultilibBuilder("/lib32")
+                   .osSuffix("")
+                   .flag("+mabi=n32")
+                   .flag("-mabi=n64");
+    auto N64 = MultilibBuilder("/lib64")
+                   .osSuffix("")
+                   .flag("-mabi=n32")
+                   .flag("+mabi=n64");
 
     ImgMultilibsV2 =
-        MultilibSet()
+        MultilibSetBuilder()
             .Either({BeHard, BeSoft, ElHard, ElSoft, BeMicroHard, BeMicroSoft,
                      ElMicroHard, ElMicroSoft})
             .Either(O32, N32, N64)
+            .makeMultilibSet()
             .FilterOut(NonExistent)
             .setIncludeDirsCallback([](const Multilib &M) {
               return std::vector<std::string>({"/../../../../sysroot" +
@@ -1556,22 +1584,19 @@
                                     DetectedMultilibs &Result) {
   // Find multilibs with subdirectories like armv7-a, thumb, armv7-a/thumb.
   FilterNonExistent NonExistent(Path, "/crtbegin.o", D.getVFS());
-  Multilib ArmV7Multilib = makeMultilib("/armv7-a")
-                               .flag("+march=armv7-a")
-                               .flag("-mthumb");
-  Multilib ThumbMultilib = makeMultilib("/thumb")
-                               .flag("-march=armv7-a")
-                               .flag("+mthumb");
-  Multilib ArmV7ThumbMultilib = makeMultilib("/armv7-a/thumb")
-                               .flag("+march=armv7-a")
-                               .flag("+mthumb");
-  Multilib DefaultMultilib = makeMultilib("")
-                               .flag("-march=armv7-a")
-                               .flag("-mthumb");
+  MultilibBuilder ArmV7Multilib =
+      MultilibBuilder("/armv7-a").flag("+march=armv7-a").flag("-mthumb");
+  MultilibBuilder ThumbMultilib =
+      MultilibBuilder("/thumb").flag("-march=armv7-a").flag("+mthumb");
+  MultilibBuilder ArmV7ThumbMultilib =
+      MultilibBuilder("/armv7-a/thumb").flag("+march=armv7-a").flag("+mthumb");
+  MultilibBuilder DefaultMultilib =
+      MultilibBuilder("").flag("-march=armv7-a").flag("-mthumb");
   MultilibSet AndroidArmMultilibs =
-      MultilibSet()
-          .Either(ThumbMultilib, ArmV7Multilib,
-                  ArmV7ThumbMultilib, DefaultMultilib)
+      MultilibSetBuilder()
+          .Either(ThumbMultilib, ArmV7Multilib, ArmV7ThumbMultilib,
+                  DefaultMultilib)
+          .makeMultilibSet()
           .FilterOut(NonExistent);
 
   Multilib::flags_list Flags;
@@ -1597,15 +1622,17 @@
                                 StringRef Path, const ArgList &Args,
                                 DetectedMultilibs &Result) {
   FilterNonExistent NonExistent(Path, "/crtbegin.o", D.getVFS());
-  Multilib WithoutExceptions = makeMultilib("/430").flag("-exceptions");
-  Multilib WithExceptions = makeMultilib("/430/exceptions").flag("+exceptions");
+  MultilibBuilder WithoutExceptions =
+      MultilibBuilder("/430").flag("-exceptions");
+  MultilibBuilder WithExceptions =
+      MultilibBuilder("/430/exceptions").flag("+exceptions");
 
   // FIXME: when clang starts to support msp430x ISA additional logic
   // to select between multilib must be implemented
-  // Multilib MSP430xMultilib = makeMultilib("/large");
+  // MultilibBuilder MSP430xMultilib = MultilibBuilder("/large");
 
-  Result.Multilibs.push_back(WithoutExceptions);
-  Result.Multilibs.push_back(WithExceptions);
+  Result.Multilibs.push_back(WithoutExceptions.makeMultilib());
+  Result.Multilibs.push_back(WithExceptions.makeMultilib());
   Result.Multilibs.FilterOut(NonExistent);
 
   Multilib::flags_list Flags;
@@ -1653,28 +1680,29 @@
     isBigEndian = !A->getOption().matches(options::OPT_mlittle_endian);
   addMultilibFlag(isBigEndian, "EB", Flags);
 
-  auto HardFloat = makeMultilib("/hard-fp").flag("+hard-fp");
-  auto SoftFpFloat = makeMultilib("/soft-fp").flag("+soft-fp");
-  auto SoftFloat = makeMultilib("").flag("+soft");
-  auto Arch801 = makeMultilib("/ck801").flag("+march=ck801");
-  auto Arch802 = makeMultilib("/ck802").flag("+march=ck802");
-  auto Arch803 = makeMultilib("/ck803").flag("+march=ck803");
+  auto HardFloat = MultilibBuilder("/hard-fp").flag("+hard-fp");
+  auto SoftFpFloat = MultilibBuilder("/soft-fp").flag("+soft-fp");
+  auto SoftFloat = MultilibBuilder("").flag("+soft");
+  auto Arch801 = MultilibBuilder("/ck801").flag("+march=ck801");
+  auto Arch802 = MultilibBuilder("/ck802").flag("+march=ck802");
+  auto Arch803 = MultilibBuilder("/ck803").flag("+march=ck803");
   // CK804 use the same library as CK803
-  auto Arch804 = makeMultilib("/ck803").flag("+march=ck804");
-  auto Arch805 = makeMultilib("/ck805").flag("+march=ck805");
-  auto Arch807 = makeMultilib("/ck807").flag("+march=ck807");
-  auto Arch810 = makeMultilib("").flag("+march=ck810");
-  auto Arch810v = makeMultilib("/ck810v").flag("+march=ck810v");
-  auto Arch860 = makeMultilib("/ck860").flag("+march=ck860");
-  auto Arch860v = makeMultilib("/ck860v").flag("+march=ck860v");
-  auto BigEndian = makeMultilib("/big").flag("+EB");
+  auto Arch804 = MultilibBuilder("/ck803").flag("+march=ck804");
+  auto Arch805 = MultilibBuilder("/ck805").flag("+march=ck805");
+  auto Arch807 = MultilibBuilder("/ck807").flag("+march=ck807");
+  auto Arch810 = MultilibBuilder("").flag("+march=ck810");
+  auto Arch810v = MultilibBuilder("/ck810v").flag("+march=ck810v");
+  auto Arch860 = MultilibBuilder("/ck860").flag("+march=ck860");
+  auto Arch860v = MultilibBuilder("/ck860v").flag("+march=ck860v");
+  auto BigEndian = MultilibBuilder("/big").flag("+EB");
 
   MultilibSet CSKYMultilibs =
-      MultilibSet()
+      MultilibSetBuilder()
           .Maybe(BigEndian)
           .Either({Arch801, Arch802, Arch803, Arch804, Arch805, Arch807,
                    Arch810, Arch810v, Arch860, Arch860v})
           .Either(HardFloat, SoftFpFloat, SoftFloat)
+          .makeMultilibSet()
           .FilterOut(NonExistent);
 
   if (CSKYMultilibs.select(Flags, Result.SelectedMultilib))
@@ -1697,17 +1725,19 @@
       {"rv32imac", "ilp32"},  {"rv32imafc", "ilp32f"}, {"rv64imac", "lp64"},
       {"rv64imafdc", "lp64d"}};
 
-  std::vector<Multilib> Ms;
+  std::vector<MultilibBuilder> Ms;
   for (auto Element : RISCVMultilibSet) {
     // multilib path rule is ${march}/${mabi}
     Ms.emplace_back(
-        makeMultilib((Twine(Element.march) + "/" + Twine(Element.mabi)).str())
+        MultilibBuilder(
+            (Twine(Element.march) + "/" + Twine(Element.mabi)).str())
             .flag(Twine("+march=", Element.march).str())
             .flag(Twine("+mabi=", Element.mabi).str()));
   }
   MultilibSet RISCVMultilibs =
-      MultilibSet()
-          .Either(ArrayRef<Multilib>(Ms))
+      MultilibSetBuilder()
+          .Either(Ms)
+          .makeMultilibSet()
           .FilterOut(NonExistent)
           .setFilePathsCallback([](const Multilib &M) {
             return std::vector<std::string>(
@@ -1716,7 +1746,6 @@
                  "/../../../../riscv32-unknown-elf/lib" + M.gccSuffix()});
           });
 
-
   Multilib::flags_list Flags;
   llvm::StringSet<> Added_ABIs;
   StringRef ABIName = tools::riscv::getRISCVABI(Args, TargetTriple);
@@ -1742,17 +1771,22 @@
     return findRISCVBareMetalMultilibs(D, TargetTriple, Path, Args, Result);
 
   FilterNonExistent NonExistent(Path, "/crtbegin.o", D.getVFS());
-  Multilib Ilp32 = makeMultilib("lib32/ilp32").flag("+m32").flag("+mabi=ilp32");
-  Multilib Ilp32f =
-      makeMultilib("lib32/ilp32f").flag("+m32").flag("+mabi=ilp32f");
-  Multilib Ilp32d =
-      makeMultilib("lib32/ilp32d").flag("+m32").flag("+mabi=ilp32d");
-  Multilib Lp64 = makeMultilib("lib64/lp64").flag("+m64").flag("+mabi=lp64");
-  Multilib Lp64f = makeMultilib("lib64/lp64f").flag("+m64").flag("+mabi=lp64f");
-  Multilib Lp64d = makeMultilib("lib64/lp64d").flag("+m64").flag("+mabi=lp64d");
+  MultilibBuilder Ilp32 =
+      MultilibBuilder("lib32/ilp32").flag("+m32").flag("+mabi=ilp32");
+  MultilibBuilder Ilp32f =
+      MultilibBuilder("lib32/ilp32f").flag("+m32").flag("+mabi=ilp32f");
+  MultilibBuilder Ilp32d =
+      MultilibBuilder("lib32/ilp32d").flag("+m32").flag("+mabi=ilp32d");
+  MultilibBuilder Lp64 =
+      MultilibBuilder("lib64/lp64").flag("+m64").flag("+mabi=lp64");
+  MultilibBuilder Lp64f =
+      MultilibBuilder("lib64/lp64f").flag("+m64").flag("+mabi=lp64f");
+  MultilibBuilder Lp64d =
+      MultilibBuilder("lib64/lp64d").flag("+m64").flag("+mabi=lp64d");
   MultilibSet RISCVMultilibs =
-      MultilibSet()
+      MultilibSetBuilder()
           .Either({Ilp32, Ilp32f, Ilp32d, Lp64, Lp64f, Lp64d})
+          .makeMultilibSet()
           .FilterOut(NonExistent);
 
   Multilib::flags_list Flags;
@@ -1777,7 +1811,7 @@
                                 StringRef Path, const ArgList &Args,
                                 bool NeedsBiarchSuffix,
                                 DetectedMultilibs &Result) {
-  Multilib Default;
+  MultilibBuilder DefaultBuilder;
 
   // Some versions of SUSE and Fedora on ppc64 put 32-bit libs
   // in what would normally be GCCInstallPath and put the 64-bit
@@ -1803,24 +1837,27 @@
     }
   }
 
-  Multilib Alt64 = Multilib()
+  Multilib Alt64 = MultilibBuilder()
                        .gccSuffix(Suff64)
                        .includeSuffix(Suff64)
                        .flag("-m32")
                        .flag("+m64")
-                       .flag("-mx32");
-  Multilib Alt32 = Multilib()
+                       .flag("-mx32")
+                       .makeMultilib();
+  Multilib Alt32 = MultilibBuilder()
                        .gccSuffix("/32")
                        .includeSuffix("/32")
                        .flag("+m32")
                        .flag("-m64")
-                       .flag("-mx32");
-  Multilib Altx32 = Multilib()
+                       .flag("-mx32")
+                       .makeMultilib();
+  Multilib Altx32 = MultilibBuilder()
                         .gccSuffix("/x32")
                         .includeSuffix("/x32")
                         .flag("-m32")
                         .flag("-m64")
-                        .flag("+mx32");
+                        .flag("+mx32")
+                        .makeMultilib();
 
   // GCC toolchain for IAMCU doesn't have crtbegin.o, so look for libgcc.a.
   FilterNonExistent NonExistent(
@@ -1846,14 +1883,16 @@
   }
 
   if (Want == WANT32)
-    Default.flag("+m32").flag("-m64").flag("-mx32");
+    DefaultBuilder.flag("+m32").flag("-m64").flag("-mx32");
   else if (Want == WANT64)
-    Default.flag("-m32").flag("+m64").flag("-mx32");
+    DefaultBuilder.flag("-m32").flag("+m64").flag("-mx32");
   else if (Want == WANTX32)
-    Default.flag("-m32").flag("-m64").flag("+mx32");
+    DefaultBuilder.flag("-m32").flag("-m64").flag("+mx32");
   else
     return false;
 
+  Multilib Default = DefaultBuilder.makeMultilib();
+
   Result.Multilibs.push_back(Default);
   Result.Multilibs.push_back(Alt64);
   Result.Multilibs.push_back(Alt32);
Index: clang/lib/Driver/ToolChains/Fuchsia.cpp
===================================================================
--- clang/lib/Driver/ToolChains/Fuchsia.cpp
+++ clang/lib/Driver/ToolChains/Fuchsia.cpp
@@ -12,6 +12,7 @@
 #include "clang/Driver/Compilation.h"
 #include "clang/Driver/Driver.h"
 #include "clang/Driver/DriverDiagnostic.h"
+#include "clang/Driver/MultilibBuilder.h"
 #include "clang/Driver/Options.h"
 #include "clang/Driver/SanitizerArgs.h"
 #include "llvm/Option/ArgList.h"
@@ -262,28 +263,35 @@
 
   Multilibs.push_back(Multilib());
   // Use the noexcept variant with -fno-exceptions to avoid the extra overhead.
-  Multilibs.push_back(Multilib("noexcept", {}, {}, 1)
+  Multilibs.push_back(MultilibBuilder("noexcept", {}, {}, 1)
                           .flag("-fexceptions")
-                          .flag("+fno-exceptions"));
+                          .flag("+fno-exceptions")
+                          .makeMultilib());
   // ASan has higher priority because we always want the instrumentated version.
-  Multilibs.push_back(Multilib("asan", {}, {}, 2)
-                          .flag("+fsanitize=address"));
+  Multilibs.push_back(MultilibBuilder("asan", {}, {}, 2)
+                          .flag("+fsanitize=address")
+                          .makeMultilib());
   // Use the asan+noexcept variant with ASan and -fno-exceptions.
-  Multilibs.push_back(Multilib("asan+noexcept", {}, {}, 3)
+  Multilibs.push_back(MultilibBuilder("asan+noexcept", {}, {}, 3)
                           .flag("+fsanitize=address")
                           .flag("-fexceptions")
-                          .flag("+fno-exceptions"));
+                          .flag("+fno-exceptions")
+                          .makeMultilib());
   // HWASan has higher priority because we always want the instrumentated
   // version.
-  Multilibs.push_back(
-      Multilib("hwasan", {}, {}, 4).flag("+fsanitize=hwaddress"));
+  Multilibs.push_back(MultilibBuilder("hwasan", {}, {}, 4)
+                          .flag("+fsanitize=hwaddress")
+                          .makeMultilib());
   // Use the hwasan+noexcept variant with HWASan and -fno-exceptions.
-  Multilibs.push_back(Multilib("hwasan+noexcept", {}, {}, 5)
+  Multilibs.push_back(MultilibBuilder("hwasan+noexcept", {}, {}, 5)
                           .flag("+fsanitize=hwaddress")
                           .flag("-fexceptions")
-                          .flag("+fno-exceptions"));
+                          .flag("+fno-exceptions")
+                          .makeMultilib());
   // Use Itanium C++ ABI for the compat multilib.
-  Multilibs.push_back(Multilib("compat", {}, {}, 6).flag("+fc++-abi=itanium"));
+  Multilibs.push_back(MultilibBuilder("compat", {}, {}, 6)
+                          .flag("+fc++-abi=itanium")
+                          .makeMultilib());
 
   Multilibs.FilterOut([&](const Multilib &M) {
     std::vector<std::string> RD = FilePaths(M);
Index: clang/lib/Driver/ToolChains/BareMetal.cpp
===================================================================
--- clang/lib/Driver/ToolChains/BareMetal.cpp
+++ clang/lib/Driver/ToolChains/BareMetal.cpp
@@ -16,6 +16,7 @@
 #include "clang/Driver/Compilation.h"
 #include "clang/Driver/Driver.h"
 #include "clang/Driver/DriverDiagnostic.h"
+#include "clang/Driver/MultilibBuilder.h"
 #include "clang/Driver/Options.h"
 #include "llvm/Option/ArgList.h"
 #include "llvm/Support/Path.h"
@@ -28,10 +29,6 @@
 using namespace clang::driver::tools;
 using namespace clang::driver::toolchains;
 
-static Multilib makeMultilib(StringRef commonSuffix) {
-  return Multilib(commonSuffix, commonSuffix, commonSuffix);
-}
-
 static bool findRISCVMultilibs(const Driver &D,
                                const llvm::Triple &TargetTriple,
                                const ArgList &Args, DetectedMultilibs &Result) {
@@ -40,10 +37,11 @@
   StringRef Abi = tools::riscv::getRISCVABI(Args, TargetTriple);
 
   if (TargetTriple.isRISCV64()) {
-    Multilib Imac = makeMultilib("").flag("+march=rv64imac").flag("+mabi=lp64");
-    Multilib Imafdc = makeMultilib("/rv64imafdc/lp64d")
-                          .flag("+march=rv64imafdc")
-                          .flag("+mabi=lp64d");
+    MultilibBuilder Imac =
+        MultilibBuilder().flag("+march=rv64imac").flag("+mabi=lp64");
+    MultilibBuilder Imafdc = MultilibBuilder("/rv64imafdc/lp64d")
+                                 .flag("+march=rv64imafdc")
+                                 .flag("+mabi=lp64d");
 
     // Multilib reuse
     bool UseImafdc =
@@ -54,22 +52,25 @@
     addMultilibFlag(Abi == "lp64", "mabi=lp64", Flags);
     addMultilibFlag(Abi == "lp64d", "mabi=lp64d", Flags);
 
-    Result.Multilibs = MultilibSet().Either(Imac, Imafdc);
+    Result.Multilibs =
+        MultilibSetBuilder().Either(Imac, Imafdc).makeMultilibSet();
     return Result.Multilibs.select(Flags, Result.SelectedMultilib);
   }
   if (TargetTriple.isRISCV32()) {
-    Multilib Imac =
-        makeMultilib("").flag("+march=rv32imac").flag("+mabi=ilp32");
-    Multilib I =
-        makeMultilib("/rv32i/ilp32").flag("+march=rv32i").flag("+mabi=ilp32");
-    Multilib Im =
-        makeMultilib("/rv32im/ilp32").flag("+march=rv32im").flag("+mabi=ilp32");
-    Multilib Iac = makeMultilib("/rv32iac/ilp32")
-                       .flag("+march=rv32iac")
-                       .flag("+mabi=ilp32");
-    Multilib Imafc = makeMultilib("/rv32imafc/ilp32f")
-                         .flag("+march=rv32imafc")
-                         .flag("+mabi=ilp32f");
+    MultilibBuilder Imac =
+        MultilibBuilder().flag("+march=rv32imac").flag("+mabi=ilp32");
+    MultilibBuilder I = MultilibBuilder("/rv32i/ilp32")
+                            .flag("+march=rv32i")
+                            .flag("+mabi=ilp32");
+    MultilibBuilder Im = MultilibBuilder("/rv32im/ilp32")
+                             .flag("+march=rv32im")
+                             .flag("+mabi=ilp32");
+    MultilibBuilder Iac = MultilibBuilder("/rv32iac/ilp32")
+                              .flag("+march=rv32iac")
+                              .flag("+mabi=ilp32");
+    MultilibBuilder Imafc = MultilibBuilder("/rv32imafc/ilp32f")
+                                .flag("+march=rv32imafc")
+                                .flag("+mabi=ilp32f");
 
     // Multilib reuse
     bool UseI = (Arch == "rv32i") || (Arch == "rv32ic");    // ic => i
@@ -85,7 +86,8 @@
     addMultilibFlag(Abi == "ilp32", "mabi=ilp32", Flags);
     addMultilibFlag(Abi == "ilp32f", "mabi=ilp32f", Flags);
 
-    Result.Multilibs = MultilibSet().Either(I, Im, Iac, Imac, Imafc);
+    Result.Multilibs =
+        MultilibSetBuilder().Either(I, Im, Iac, Imac, Imafc).makeMultilibSet();
     return Result.Multilibs.select(Flags, Result.SelectedMultilib);
   }
   return false;
Index: clang/lib/Driver/MultilibBuilder.cpp
===================================================================
--- /dev/null
+++ clang/lib/Driver/MultilibBuilder.cpp
@@ -0,0 +1,192 @@
+//===- MultilibBuilder.cpp - MultilibBuilder Implementation -===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang/Driver/MultilibBuilder.h"
+#include "llvm/ADT/SmallString.h"
+#include "llvm/ADT/StringMap.h"
+#include "llvm/Support/Path.h"
+#include "llvm/Support/Regex.h"
+#include "llvm/Support/raw_ostream.h"
+
+using namespace clang;
+using namespace driver;
+
+/// normalize Segment to "/foo/bar" or "".
+static void normalizePathSegment(std::string &Segment) {
+  StringRef seg = Segment;
+
+  // Prune trailing "/" or "./"
+  while (true) {
+    StringRef last = llvm::sys::path::filename(seg);
+    if (last != ".")
+      break;
+    seg = llvm::sys::path::parent_path(seg);
+  }
+
+  if (seg.empty() || seg == "/") {
+    Segment.clear();
+    return;
+  }
+
+  // Add leading '/'
+  if (seg.front() != '/') {
+    Segment = "/" + seg.str();
+  } else {
+    Segment = std::string(seg);
+  }
+}
+
+MultilibBuilder::MultilibBuilder(StringRef GCC, StringRef OS, StringRef Include,
+                                 int Priority)
+    : GCCSuffix(GCC), OSSuffix(OS), IncludeSuffix(Include), Priority(Priority) {
+  normalizePathSegment(GCCSuffix);
+  normalizePathSegment(OSSuffix);
+  normalizePathSegment(IncludeSuffix);
+}
+
+MultilibBuilder::MultilibBuilder(StringRef Suffix)
+    : MultilibBuilder(Suffix, Suffix, Suffix) {}
+
+MultilibBuilder &MultilibBuilder::gccSuffix(StringRef S) {
+  GCCSuffix = std::string(S);
+  normalizePathSegment(GCCSuffix);
+  return *this;
+}
+
+MultilibBuilder &MultilibBuilder::osSuffix(StringRef S) {
+  OSSuffix = std::string(S);
+  normalizePathSegment(OSSuffix);
+  return *this;
+}
+
+MultilibBuilder &MultilibBuilder::includeSuffix(StringRef S) {
+  IncludeSuffix = std::string(S);
+  normalizePathSegment(IncludeSuffix);
+  return *this;
+}
+
+bool MultilibBuilder::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;
+}
+
+Multilib MultilibBuilder::makeMultilib() const {
+  return Multilib(GCCSuffix, OSSuffix, IncludeSuffix, Priority, Flags);
+}
+
+MultilibSetBuilder &MultilibSetBuilder::Maybe(const MultilibBuilder &M) {
+  MultilibBuilder Opposite;
+  // Negate any '+' flags
+  for (StringRef Flag : M.flags()) {
+    if (Flag.front() == '+')
+      Opposite.flags().push_back(("-" + Flag.substr(1)).str());
+  }
+  return Either(M, Opposite);
+}
+
+MultilibSetBuilder &MultilibSetBuilder::Either(const MultilibBuilder &M1,
+                                               const MultilibBuilder &M2) {
+  return Either({M1, M2});
+}
+
+MultilibSetBuilder &MultilibSetBuilder::Either(const MultilibBuilder &M1,
+                                               const MultilibBuilder &M2,
+                                               const MultilibBuilder &M3) {
+  return Either({M1, M2, M3});
+}
+
+MultilibSetBuilder &MultilibSetBuilder::Either(const MultilibBuilder &M1,
+                                               const MultilibBuilder &M2,
+                                               const MultilibBuilder &M3,
+                                               const MultilibBuilder &M4) {
+  return Either({M1, M2, M3, M4});
+}
+
+MultilibSetBuilder &MultilibSetBuilder::Either(const MultilibBuilder &M1,
+                                               const MultilibBuilder &M2,
+                                               const MultilibBuilder &M3,
+                                               const MultilibBuilder &M4,
+                                               const MultilibBuilder &M5) {
+  return Either({M1, M2, M3, M4, M5});
+}
+
+static MultilibBuilder compose(const MultilibBuilder &Base,
+                               const MultilibBuilder &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());
+
+  MultilibBuilder Composed(GCCSuffix, OSSuffix, IncludeSuffix);
+
+  MultilibBuilder::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;
+}
+
+MultilibSetBuilder &
+MultilibSetBuilder::Either(ArrayRef<MultilibBuilder> MultilibSegments) {
+  multilib_list Composed;
+
+  if (Multilibs.empty())
+    Multilibs.insert(Multilibs.end(), MultilibSegments.begin(),
+                     MultilibSegments.end());
+  else {
+    for (const auto &New : MultilibSegments) {
+      for (const auto &Base : Multilibs) {
+        MultilibBuilder MO = compose(Base, New);
+        if (MO.isValid())
+          Composed.push_back(MO);
+      }
+    }
+
+    Multilibs = Composed;
+  }
+
+  return *this;
+}
+
+MultilibSetBuilder &MultilibSetBuilder::FilterOut(const char *Regex) {
+  llvm::Regex R(Regex);
+#ifndef NDEBUG
+  std::string Error;
+  if (!R.isValid(Error)) {
+    llvm::errs() << Error;
+    llvm_unreachable("Invalid regex!");
+  }
+#endif
+  llvm::erase_if(Multilibs, [&R](const MultilibBuilder &M) {
+    return R.match(M.gccSuffix());
+  });
+  return *this;
+}
+
+MultilibSet MultilibSetBuilder::makeMultilibSet() const {
+  MultilibSet Result;
+  for (const auto &M : Multilibs) {
+    Result.push_back(M.makeMultilib());
+  }
+  return Result;
+}
Index: clang/lib/Driver/Multilib.cpp
===================================================================
--- clang/lib/Driver/Multilib.cpp
+++ clang/lib/Driver/Multilib.cpp
@@ -25,56 +25,17 @@
 using namespace driver;
 using namespace llvm::sys;
 
-/// normalize Segment to "/foo/bar" or "".
-static void normalizePathSegment(std::string &Segment) {
-  StringRef seg = Segment;
-
-  // Prune trailing "/" or "./"
-  while (true) {
-    StringRef last = path::filename(seg);
-    if (last != ".")
-      break;
-    seg = path::parent_path(seg);
-  }
-
-  if (seg.empty() || seg == "/") {
-    Segment.clear();
-    return;
-  }
-
-  // Add leading '/'
-  if (seg.front() != '/') {
-    Segment = "/" + seg.str();
-  } else {
-    Segment = std::string(seg);
-  }
-}
-
 Multilib::Multilib(StringRef GCCSuffix, StringRef OSSuffix,
-                   StringRef IncludeSuffix, int Priority)
+                   StringRef IncludeSuffix, int Priority,
+                   const flags_list &Flags)
     : GCCSuffix(GCCSuffix), OSSuffix(OSSuffix), IncludeSuffix(IncludeSuffix),
-      Priority(Priority) {
-  normalizePathSegment(this->GCCSuffix);
-  normalizePathSegment(this->OSSuffix);
-  normalizePathSegment(this->IncludeSuffix);
-}
-
-Multilib &Multilib::gccSuffix(StringRef S) {
-  GCCSuffix = std::string(S);
-  normalizePathSegment(GCCSuffix);
-  return *this;
-}
-
-Multilib &Multilib::osSuffix(StringRef S) {
-  OSSuffix = std::string(S);
-  normalizePathSegment(OSSuffix);
-  return *this;
-}
-
-Multilib &Multilib::includeSuffix(StringRef S) {
-  IncludeSuffix = std::string(S);
-  normalizePathSegment(IncludeSuffix);
-  return *this;
+      Flags(Flags), Priority(Priority) {
+  assert(GCCSuffix.empty() ||
+         (StringRef(GCCSuffix).front() == '/' && GCCSuffix.size() > 1));
+  assert(OSSuffix.empty() ||
+         (StringRef(OSSuffix).front() == '/' && OSSuffix.size() > 1));
+  assert(IncludeSuffix.empty() ||
+         (StringRef(IncludeSuffix).front() == '/' && IncludeSuffix.size() > 1));
 }
 
 LLVM_DUMP_METHOD void Multilib::dump() const {
@@ -82,7 +43,6 @@
 }
 
 void Multilib::print(raw_ostream &OS) const {
-  assert(GCCSuffix.empty() || (StringRef(GCCSuffix).front() == '/'));
   if (GCCSuffix.empty())
     OS << ".";
   else {
@@ -95,22 +55,6 @@
   }
 }
 
-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
@@ -139,102 +83,13 @@
   return OS;
 }
 
-MultilibSet &MultilibSet::Maybe(const Multilib &M) {
-  Multilib Opposite;
-  // Negate any '+' flags
-  for (StringRef Flag : M.flags()) {
-    if (Flag.front() == '+')
-      Opposite.flags().push_back(("-" + Flag.substr(1)).str());
-  }
-  return Either(M, Opposite);
-}
-
-MultilibSet &MultilibSet::Either(const Multilib &M1, const Multilib &M2) {
-  return Either({M1, M2});
-}
-
-MultilibSet &MultilibSet::Either(const Multilib &M1, const Multilib &M2,
-                                 const Multilib &M3) {
-  return Either({M1, M2, M3});
-}
-
-MultilibSet &MultilibSet::Either(const Multilib &M1, const Multilib &M2,
-                                 const Multilib &M3, const Multilib &M4) {
-  return Either({M1, M2, M3, M4});
-}
-
-MultilibSet &MultilibSet::Either(const Multilib &M1, const Multilib &M2,
-                                 const Multilib &M3, const Multilib &M4,
-                                 const Multilib &M5) {
-  return Either({M1, M2, M3, M4, M5});
-}
-
-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, OSSuffix, IncludeSuffix);
-
-  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(ArrayRef<Multilib> MultilibSegments) {
-  multilib_list Composed;
-
-  if (Multilibs.empty())
-    Multilibs.insert(Multilibs.end(), MultilibSegments.begin(),
-                     MultilibSegments.end());
-  else {
-    for (const auto &New : MultilibSegments) {
-      for (const auto &Base : *this) {
-        Multilib MO = compose(Base, New);
-        if (MO.isValid())
-          Composed.push_back(MO);
-      }
-    }
-
-    Multilibs = Composed;
-  }
-
-  return *this;
-}
-
 MultilibSet &MultilibSet::FilterOut(FilterCallback F) {
   filterInPlace(F, Multilibs);
   return *this;
 }
 
-MultilibSet &MultilibSet::FilterOut(const char *Regex) {
-  llvm::Regex R(Regex);
-#ifndef NDEBUG
-  std::string Error;
-  if (!R.isValid(Error)) {
-    llvm::errs() << Error;
-    llvm_unreachable("Invalid regex!");
-  }
-#endif
-
-  filterInPlace([&R](const Multilib &M) { return R.match(M.gccSuffix()); },
-                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());
-}
-
 static bool isFlagEnabled(StringRef Flag) {
   char Indicator = Flag.front();
   assert(Indicator == '+' || Indicator == '-');
Index: clang/lib/Driver/CMakeLists.txt
===================================================================
--- clang/lib/Driver/CMakeLists.txt
+++ clang/lib/Driver/CMakeLists.txt
@@ -22,6 +22,7 @@
   DriverOptions.cpp
   Job.cpp
   Multilib.cpp
+  MultilibBuilder.cpp
   OffloadBundler.cpp
   OptionUtils.cpp
   Phases.cpp
Index: clang/include/clang/Driver/MultilibBuilder.h
===================================================================
--- /dev/null
+++ clang/include/clang/Driver/MultilibBuilder.h
@@ -0,0 +1,148 @@
+//===- MultilibBuilder.h
+//-----------------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_DRIVER_MULTILIBBUILDER_H
+#define LLVM_CLANG_DRIVER_MULTILIBBUILDER_H
+
+#include "clang/Driver/Multilib.h"
+
+namespace clang {
+namespace driver {
+
+/// This corresponds to a single GCC multilib, or a segment of one controlled
+/// by a command line flag. This class can be used to create a Multilib, and
+/// contains helper functions to mutate it before creating a Multilib instance
+/// with makeMultilib().
+class MultilibBuilder {
+public:
+  using flags_list = std::vector<std::string>;
+
+private:
+  std::string GCCSuffix;
+  std::string OSSuffix;
+  std::string IncludeSuffix;
+  flags_list Flags;
+  int Priority;
+
+public:
+  MultilibBuilder(StringRef GCCSuffix, StringRef OSSuffix,
+                  StringRef IncludeSuffix, int Priority = 0);
+
+  /// Initializes GCCSuffix, OSSuffix & IncludeSuffix to the same value.
+  MultilibBuilder(StringRef Suffix = {});
+
+  /// 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.
+  MultilibBuilder &gccSuffix(StringRef S);
+
+  /// 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.
+  MultilibBuilder &osSuffix(StringRef S);
+
+  /// 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
+  MultilibBuilder &includeSuffix(StringRef S);
+
+  /// 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; }
+
+  /// Returns the multilib priority. When more than one multilib matches flags,
+  /// the one with the highest priority is selected, with 0 being the default.
+  int priority() const { return Priority; }
+
+  /// Add a flag to the flags list
+  /// \p Flag must be a flag accepted by the driver with its leading '-'
+  /// removed,
+  ///     and replaced with either:
+  ///       '-' which contraindicates using this multilib with that flag
+  ///     or:
+  ///       '+' which promotes using this multilib in the presence of that flag
+  ///     otherwise '-print-multi-lib' will not emit them correctly.
+  MultilibBuilder &flag(StringRef F) {
+    assert(F.front() == '+' || F.front() == '-');
+    Flags.push_back(std::string(F));
+    return *this;
+  }
+
+  Multilib makeMultilib() const;
+
+  /// Check whether any of the 'against' flags contradict the 'for' flags.
+  bool isValid() const;
+
+  /// Check whether the default is selected
+  bool isDefault() const {
+    return GCCSuffix.empty() && OSSuffix.empty() && IncludeSuffix.empty();
+  }
+};
+
+/// This class can be used to create a MultilibSet, and contains helper
+/// functions to add combinations of multilibs before creating a MultilibSet
+/// instance with makeMultilibSet().
+class MultilibSetBuilder {
+public:
+  using multilib_list = std::vector<MultilibBuilder>;
+
+  MultilibSetBuilder() = default;
+
+  /// Add an optional Multilib segment
+  MultilibSetBuilder &Maybe(const MultilibBuilder &M);
+
+  /// Add a set of mutually incompatible Multilib segments
+  MultilibSetBuilder &Either(const MultilibBuilder &M1,
+                             const MultilibBuilder &M2);
+  MultilibSetBuilder &Either(const MultilibBuilder &M1,
+                             const MultilibBuilder &M2,
+                             const MultilibBuilder &M3);
+  MultilibSetBuilder &Either(const MultilibBuilder &M1,
+                             const MultilibBuilder &M2,
+                             const MultilibBuilder &M3,
+                             const MultilibBuilder &M4);
+  MultilibSetBuilder &Either(const MultilibBuilder &M1,
+                             const MultilibBuilder &M2,
+                             const MultilibBuilder &M3,
+                             const MultilibBuilder &M4,
+                             const MultilibBuilder &M5);
+  MultilibSetBuilder &Either(ArrayRef<MultilibBuilder> Ms);
+
+  /// Filter out those Multilibs whose gccSuffix matches the given expression
+  MultilibSetBuilder &FilterOut(const char *Regex);
+
+  MultilibSet makeMultilibSet() const;
+
+private:
+  multilib_list Multilibs;
+};
+
+} // namespace driver
+} // namespace clang
+
+#endif // LLVM_CLANG_DRIVER_MULTILIBBUILDER_H
Index: clang/include/clang/Driver/Multilib.h
===================================================================
--- clang/include/clang/Driver/Multilib.h
+++ clang/include/clang/Driver/Multilib.h
@@ -24,7 +24,9 @@
 namespace driver {
 
 /// This corresponds to a single GCC Multilib, or a segment of one controlled
-/// by a command line flag
+/// by a command line flag.
+/// See also MultilibBuilder for building a multilib by mutating it
+/// incrementally.
 class Multilib {
 public:
   using flags_list = std::vector<std::string>;
@@ -37,71 +39,37 @@
   int Priority;
 
 public:
+  /// GCCSuffix, OSSuffix & IncludeSuffix will be appended directly to the
+  /// sysroot string so they must either be empty or begin with a '/' character.
+  /// This is enforced with an assert in the constructor.
   Multilib(StringRef GCCSuffix = {}, StringRef OSSuffix = {},
-           StringRef IncludeSuffix = {}, int Priority = 0);
+           StringRef IncludeSuffix = {}, int Priority = 0,
+           const flags_list &Flags = flags_list());
 
   /// 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);
+  const std::string &gccSuffix() const { return GCCSuffix; }
 
   /// 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);
+  const std::string &osSuffix() const { return OSSuffix; }
 
   /// 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);
+  const std::string &includeSuffix() const { return IncludeSuffix; }
 
   /// 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; }
 
   /// Returns the multilib priority. When more than one multilib matches flags,
   /// the one with the highest priority is selected, with 0 being the default.
   int priority() const { return Priority; }
 
-  /// Add a flag to the flags list
-  /// \p Flag must be a flag accepted by the driver with its leading '-' removed,
-  ///     and replaced with either:
-  ///       '-' which contraindicates using this multilib with that flag
-  ///     or:
-  ///       '+' which promotes using this multilib in the presence of that flag
-  ///     otherwise '-print-multi-lib' will not emit them correctly.
-  Multilib &flag(StringRef F) {
-    assert(F.front() == '+' || F.front() == '-');
-    Flags.push_back(std::string(F));
-    return *this;
-  }
-
   LLVM_DUMP_METHOD void dump() const;
   /// 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;
-
   /// Check whether the default is selected
   bool isDefault() const
   { return GCCSuffix.empty() && OSSuffix.empty() && IncludeSuffix.empty(); }
@@ -111,10 +79,10 @@
 
 raw_ostream &operator<<(raw_ostream &OS, const Multilib &M);
 
+/// See also MultilibSetBuilder for combining multilibs into a set.
 class MultilibSet {
 public:
   using multilib_list = std::vector<Multilib>;
-  using iterator = multilib_list::iterator;
   using const_iterator = multilib_list::const_iterator;
   using IncludeDirsFunc =
       std::function<std::vector<std::string>(const Multilib &M)>;
@@ -127,40 +95,17 @@
 
 public:
   MultilibSet() = default;
+  MultilibSet(multilib_list &&Multilibs) : Multilibs(Multilibs) {}
 
-  /// 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(ArrayRef<Multilib> Ms);
+  const multilib_list &getMultilibs() { return Multilibs; }
 
   /// Filter out some subset of the Multilibs using a user defined callback
   MultilibSet &FilterOut(FilterCallback F);
 
-  /// Filter out those Multilibs whose gccSuffix matches the given expression
-  MultilibSet &FilterOut(const char *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 the 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
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to