michaelplatings created this revision.
michaelplatings added a project: clang.
Herald added subscribers: s.egerton, ormris, abidh, mgrang, simoncook, 
kristof.beyls.
Herald added a project: All.
michaelplatings requested review of this revision.
Herald added subscribers: cfe-commits, MaskRay.

This is a prototype of a new approach to multilib.

The `multilib.yaml` file is a new interface that allows customising (a) which 
system library variants are available; and (b) how a library variant is 
identified as compatible with the clang arguments provided by the user.

The file 
clang/test/Driver/Inputs/baremetal_multilib/arm-none-eabi/multilib.yaml is an 
example that contains comments explaining how it is used.

The project I'm working on is LLVM Embedded Toolchain for Arm 
<https://github.com/ARM-software/LLVM-embedded-toolchain-for-Arm> so the design 
is made with that in mind, but it's intended to be flexible enough to work with 
other toolchains with many system library variants.


Repository:
  rG LLVM Github Monorepo

https://reviews.llvm.org/D140959

Files:
  clang/include/clang/Driver/Multilib2.h
  clang/lib/Driver/CMakeLists.txt
  clang/lib/Driver/Multilib2.cpp
  clang/lib/Driver/ToolChains/BareMetal.cpp
  clang/test/Driver/Inputs/baremetal_multilib/arm-none-eabi/multilib.yaml
  clang/test/Driver/arm-compiler-rt.c
  clang/test/Driver/baremetal-multilib.cpp
  clang/test/Driver/baremetal.cpp
  clang/test/Driver/print-libgcc-file-name-clangrt.c
  clang/unittests/Driver/CMakeLists.txt
  clang/unittests/Driver/Multilib2Test.cpp

Index: clang/unittests/Driver/Multilib2Test.cpp
===================================================================
--- /dev/null
+++ clang/unittests/Driver/Multilib2Test.cpp
@@ -0,0 +1,281 @@
+//===- unittests/Driver/Multilib2Test.cpp --- Multilib2 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
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang/Driver/Multilib2.h"
+#include "llvm/ADT/ArrayRef.h"
+#include "gtest/gtest.h"
+
+using namespace clang::driver;
+using namespace clang;
+
+TEST(Multilib2Test, Parse) {
+  std::string O;
+  llvm::raw_string_ostream OS(O);
+
+  Multilib2 M;
+  EXPECT_FALSE(M.parse(R"(
+variants:
+- path: /abc
+)", OS));
+  EXPECT_TRUE(StringRef(O).contains("paths must be relative")) << O;
+
+  EXPECT_TRUE(M.parse(R"(
+variants:
+- path: .
+)", OS));
+  EXPECT_EQ(1U, M.size());
+  EXPECT_EQ("", std::get<0>(*M.begin()));
+
+  EXPECT_TRUE(M.parse(R"(
+variants:
+- path: abc
+)", OS));
+  EXPECT_EQ(1U, M.size());
+  EXPECT_EQ("/abc", std::get<0>(*M.begin()));
+
+  EXPECT_TRUE(M.parse(R"(
+variants:
+- path: pqr
+  args: [-mfloat-abi=soft]
+)", OS));
+  EXPECT_EQ(1U, M.size());
+  EXPECT_EQ("/pqr", std::get<0>(*M.begin()));
+  EXPECT_EQ(std::vector<std::string>({"-mfloat-abi=soft"}), std::get<1>(*M.begin()));
+
+  EXPECT_TRUE(M.parse(R"(
+variants:
+- path: pqr
+  args: [-mfloat-abi=soft, -fno-exceptions]
+)", OS));
+  EXPECT_EQ(1U, M.size());
+  EXPECT_EQ(std::vector<std::string>({"-mfloat-abi=soft", "-fno-exceptions"}), std::get<1>(*M.begin()));
+
+  EXPECT_TRUE(M.parse(R"(
+variants:
+- path: a
+- path: b
+)", OS));
+  EXPECT_EQ(2U, M.size());
+}
+
+TEST(Multilib2Test, SelectSoft) {
+  std::string O;
+  llvm::raw_string_ostream OS(O);
+  Multilib2 M;
+  ASSERT_TRUE(M.parse(R"(
+variants:
+- path: s
+  attrs: [softabi]
+arguments:
+- regex: -mfloat-abi soft
+  matchAttrs: [softabi]
+- regex: -mfloat-abi hard
+  matchAttrs: [hardabi]
+- regex: -msoft-float
+  noMatchAttrs: [hasfp]
+)", OS));
+  EXPECT_TRUE(M.select({"--target=arm-none-eabi"}).first);
+  EXPECT_TRUE(M.select({"--target=arm-none-eabi", "-mfloat-abi=soft"}).first);
+  EXPECT_TRUE(M.select({"--target=arm-none-eabi", "-mfloat-abi=softfp"}).first);
+  EXPECT_FALSE(M.select({"--target=arm-none-eabi", "-mfloat-abi=hard"}).first);
+  EXPECT_FALSE(M.select({"--target=arm-none-eabihf"}).first);
+}
+
+TEST(Multilib2Test, SelectSoftFP) {
+  std::string O;
+  llvm::raw_string_ostream OS(O);
+  Multilib2 M;
+  ASSERT_TRUE(M.parse(R"(
+variants:
+- path: f
+  attrs: [softabi, hasfp]
+arguments:
+- regex: -mfloat-abi soft
+  matchAttrs: [softabi]
+- regex: -mfloat-abi hard
+  matchAttrs: [hardabi]
+- regex: -msoft-float
+  noMatchAttrs: [hasfp]
+)", OS));
+  EXPECT_FALSE(M.select({"--target=arm-none-eabi", "-mfloat-abi=soft"}).first);
+  EXPECT_TRUE(M.select({"--target=arm-none-eabi", "-mfloat-abi=softfp"}).first);
+  EXPECT_FALSE(M.select({"--target=arm-none-eabi", "-mfloat-abi=hard"}).first);
+  EXPECT_FALSE(M.select({"--target=arm-none-eabihf"}).first);
+}
+
+TEST(Multilib2Test, SelectHard) {
+  // If hard float is all that's available then select that only if compiling with hard float.
+  std::string O;
+  llvm::raw_string_ostream OS(O);
+  Multilib2 M;
+  ASSERT_TRUE(M.parse(R"(
+variants:
+- path: h
+  attrs: [hardabi, hasfp]
+arguments:
+- regex: -mfloat-abi soft
+  matchAttrs: [softabi]
+- regex: -mfloat-abi hard
+  matchAttrs: [hardabi]
+- regex: -msoft-float
+  noMatchAttrs: [hasfp]
+)", OS));
+  EXPECT_FALSE(M.select({"--target=arm-none-eabi"}).first);
+  EXPECT_FALSE(M.select({"--target=arm-none-eabi", "-mfloat-abi=soft"}).first);
+  EXPECT_FALSE(M.select({"--target=arm-none-eabi", "-mfloat-abi=softfp"}).first);
+  EXPECT_TRUE(M.select({"--target=arm-none-eabi", "-mfloat-abi=hard"}).first);
+  EXPECT_TRUE(M.select({"--target=arm-none-eabihf"}).first);
+}
+
+TEST(Multilib2Test, SelectFloatABI) {
+  std::string O;
+  llvm::raw_string_ostream OS(O);
+  Multilib2 M;
+  ASSERT_TRUE(M.parse(R"(
+variants:
+- path: s
+  attrs: [softabi]
+- path: f
+  attrs: [softabi, hasfp]
+- path: h
+  attrs: [hardabi, hasfp]
+arguments:
+- regex: -mfloat-abi soft
+  matchAttrs: [softabi]
+- regex: -mfloat-abi hard
+  matchAttrs: [hardabi]
+- regex: -msoft-float
+  noMatchAttrs: [hasfp]
+)", OS));
+  EXPECT_EQ("/f", M.select({"--target=arm-none-eabi"}).second);
+  EXPECT_EQ("/s", M.select({"--target=arm-none-eabi", "-mfloat-abi=soft"}).second);
+  EXPECT_EQ("/f", M.select({"--target=arm-none-eabi", "-mfloat-abi=softfp"}).second);
+  EXPECT_EQ("/h", M.select({"--target=arm-none-eabi", "-mfloat-abi=hard"}).second);
+  EXPECT_EQ("/h", M.select({"--target=arm-none-eabihf"}).second);
+}
+
+TEST(Multilib2Test, SelectFloatABIReversed) {
+  // If soft is specified after softfp then softfp will never be
+  // selected because soft is compatible with softfp and last wins.
+  std::string O;
+  llvm::raw_string_ostream OS(O);
+  Multilib2 M;
+  ASSERT_TRUE(M.parse(R"(
+variants:
+- path: h
+  attrs: [hardabi, hasfp]
+- path: f
+  attrs: [softabi, hasfp]
+- path: s
+  attrs: [softabi]
+arguments:
+- regex: -mfloat-abi soft
+  matchAttrs: [softabi]
+- regex: -mfloat-abi hard
+  matchAttrs: [hardabi]
+- regex: -msoft-float
+  noMatchAttrs: [hasfp]
+)", OS));
+  EXPECT_EQ("/s", M.select({"--target=arm-none-eabi"}).second);
+  EXPECT_EQ("/s", M.select({"--target=arm-none-eabi", "-mfloat-abi=soft"}).second);
+  EXPECT_EQ("/s", M.select({"--target=arm-none-eabi", "-mfloat-abi=softfp"}).second);
+  EXPECT_EQ("/h", M.select({"--target=arm-none-eabi", "-mfloat-abi=hard"}).second);
+  EXPECT_EQ("/h", M.select({"--target=arm-none-eabihf"}).second);
+}
+
+TEST(Multilib2Test, SelectMClass) {
+  const StringRef MultilibSpec = R"(
+variants:
+- path: armv6m_soft_nofp
+  args: [--target=arm-none-eabi, -mfloat-abi=soft, -march=armv6m]
+  attrs: [thumb, soft, v6m]
+- path: armv7m_soft_nofp
+  args: [--target=arm-none-eabi, -mfloat-abi=soft, -march=armv7m+nofp]
+  attrs: [thumb, soft, v7m]
+- path: armv7em_soft_nofp
+  args: [--target=arm-none-eabi, -mfloat-abi=soft, -march=armv7em, -mfpu=none]
+  attrs: [thumb, soft, v7em]
+- path: armv8m.main_soft_nofp
+  args: [--target=arm-none-eabi, -mfloat-abi=soft, -march=armv8m.main+nofp]
+  attrs: [thumb, soft, v8m.main]
+- path: armv8.1m.main_soft_nofp_nomve
+  args: [--target=arm-none-eabi, -mfloat-abi=soft, -march=armv8.1m.main+nofp+nomve]
+  attrs: [thumb, soft, v8.1m.main]
+- path: armv7em_hard_fpv4_sp_d16
+  args: [--target=arm-none-eabi, -mfloat-abi=hard, -march=armv7em, -mfpu=fpv4-sp-d16]
+  attrs: [thumb, hard, v7em, vfp2sp]
+- path: armv7em_hard_fpv5_d16
+  args: [--target=arm-none-eabi, -mfloat-abi=hard, -march=armv7em, -mfpu=fpv5-d16]
+  attrs: [thumb, hard, v7em, fp-armv8d16sp]
+- path: armv8m.main_hard_fp
+  args: [--target=arm-none-eabi, -mfloat-abi=hard, -march=armv8m.main+fp]
+  attrs: [thumb, hard, v8m.main]
+- path: armv8.1m.main_hard_fp
+  args: [--target=arm-none-eabi, -mfloat-abi=hard, -march=armv8.1m.main+fp]
+  attrs: [thumb, hard, v8.1m.main, vfp2sp]
+- path: armv8.1m.main_hard_nofp_mve
+  args: [--target=arm-none-eabi, -mfloat-abi=hard, -march=armv8.1m.main+nofp+mve]
+  attrs: [thumb, hard, v8.1m.main, mve]
+arguments:
+- regex: -triple aarch64.*
+  matchAttrs: [aarch64]
+- regex: -triple thumb.*
+  matchAttrs: [thumb]
+- regex: -mfloat-abi soft
+  matchAttrs: [soft]
+- regex: -mfloat-abi hard
+  matchAttrs: [hard]
+- regex: -target-feature -fpregs
+  noMatchAttrs: [fpregs]
+- regex: -target-feature \+vfp2sp
+  matchAttrs: [vfp2sp]
+- regex: -target-feature \+fp-armv8d16sp
+  matchAttrs: [fp-armv8d16sp]
+- regex: -target-feature \+mve
+  matchAttrs: [mve]
+- regex: -triple .*v6m-.*
+  matchAttrs: [v6m]
+- regex: -triple .*v7m-.*
+  matchAttrs: [v7m, v6m]
+- regex: -triple .*v7em-.*
+  matchAttrs: [v7em, v7m, v6m]
+- regex: -triple .*v8m\.base-.*
+  matchAttrs: [v8m.base, v6m]
+- regex: -triple .*v8m\.main-.*
+  matchAttrs: [v8m.main, v7em, v7m, v6m]
+- regex: -triple .*v8\.[0-9]+m\.base-.*
+  matchAttrs: [v8m.base, v6m]
+- regex: -triple .*v8\.[0-9]+m\.main-.*
+  matchAttrs: [v8.1m.main, v8m.main, v7em, v7m, v6m]
+)";
+
+  std::string O;
+  llvm::raw_string_ostream OS(O);
+  Multilib2 M;
+  ASSERT_TRUE(M.parse(MultilibSpec, OS));
+
+  EXPECT_EQ((std::pair<bool, std::string>(true, "/armv6m_soft_nofp")), M.select({"--target=arm-none-eabi", "-mfloat-abi=soft", "-march=armv6m"}));
+  EXPECT_EQ((std::pair<bool, std::string>(true, "/armv7m_soft_nofp")), M.select({"--target=arm-none-eabi", "-mfloat-abi=soft", "-march=armv7m+nofp"}));
+  EXPECT_EQ((std::pair<bool, std::string>(true, "/armv7em_soft_nofp")), M.select({"--target=arm-none-eabi", "-mfloat-abi=soft", "-march=armv7em", "-mfpu=none"}));
+  EXPECT_EQ((std::pair<bool, std::string>(true, "/armv8m.main_soft_nofp")), M.select({"--target=arm-none-eabi", "-mfloat-abi=soft", "-march=armv8m.main+nofp"}));
+  EXPECT_EQ((std::pair<bool, std::string>(true, "/armv8.1m.main_soft_nofp_nomve")), M.select({"--target=arm-none-eabi", "-mfloat-abi=soft", "-march=armv8.1m.main+nofp+nomve"}));
+  EXPECT_EQ((std::pair<bool, std::string>(true, "/armv7em_hard_fpv4_sp_d16")), M.select({"--target=arm-none-eabi", "-mfloat-abi=hard", "-march=armv7em", "-mfpu=fpv4-sp-d16"}));
+  EXPECT_EQ((std::pair<bool, std::string>(true, "/armv7em_hard_fpv5_d16")), M.select({"--target=arm-none-eabi", "-mfloat-abi=hard", "-march=armv7em", "-mfpu=fpv5-d16"}));
+  EXPECT_EQ((std::pair<bool, std::string>(true, "/armv8m.main_hard_fp")), M.select({"--target=arm-none-eabi", "-mfloat-abi=hard", "-march=armv8m.main+fp"}));
+  EXPECT_EQ((std::pair<bool, std::string>(true, "/armv8.1m.main_hard_fp")), M.select({"--target=arm-none-eabi", "-mfloat-abi=hard", "-march=armv8.1m.main+fp"}));
+  EXPECT_EQ((std::pair<bool, std::string>(true, "/armv8.1m.main_hard_nofp_mve")), M.select({"--target=arm-none-eabi", "-mfloat-abi=hard", "-march=armv8.1m.main+nofp+mve"}));
+  EXPECT_EQ((std::pair<bool, std::string>(true, "/armv6m_soft_nofp")), M.select({"--target=arm-none-eabi", "-mcpu=cortex-m0"}));
+  EXPECT_EQ((std::pair<bool, std::string>(true, "/armv6m_soft_nofp")), M.select({"--target=arm-none-eabi", "-mcpu=cortex-m0+"}));
+  EXPECT_EQ((std::pair<bool, std::string>(true, "/armv6m_soft_nofp")), M.select({"--target=arm-none-eabi", "-mcpu=cortex-m1"}));
+  EXPECT_EQ((std::pair<bool, std::string>(true, "/armv7m_soft_nofp")), M.select({"--target=arm-none-eabi", "-mcpu=cortex-m3"}));
+  EXPECT_EQ((std::pair<bool, std::string>(true, "/armv7em_hard_fpv4_sp_d16")), M.select({"--target=arm-none-eabihf", "-mcpu=cortex-m4"}));
+  EXPECT_EQ((std::pair<bool, std::string>(true, "/armv7em_hard_fpv5_d16")), M.select({"--target=arm-none-eabihf", "-mcpu=cortex-m7"}));
+  EXPECT_EQ((std::pair<bool, std::string>(true, "/armv6m_soft_nofp")), M.select({"--target=arm-none-eabi", "-mcpu=cortex-m23"}));
+  EXPECT_EQ((std::pair<bool, std::string>(true, "/armv8m.main_hard_fp")), M.select({"--target=arm-none-eabihf", "-mcpu=cortex-m33"}));
+  EXPECT_EQ((std::pair<bool, std::string>(true, "/armv8.1m.main_hard_nofp_mve")), M.select({"--target=arm-none-eabihf", "-mcpu=cortex-m55"}));
+}
Index: clang/unittests/Driver/CMakeLists.txt
===================================================================
--- clang/unittests/Driver/CMakeLists.txt
+++ clang/unittests/Driver/CMakeLists.txt
@@ -12,6 +12,7 @@
   ToolChainTest.cpp
   ModuleCacheTest.cpp
   MultilibTest.cpp
+  Multilib2Test.cpp
   SanitizerArgsTest.cpp
   )
 
Index: clang/test/Driver/print-libgcc-file-name-clangrt.c
===================================================================
--- clang/test/Driver/print-libgcc-file-name-clangrt.c
+++ clang/test/Driver/print-libgcc-file-name-clangrt.c
@@ -45,6 +45,7 @@
 
 // RUN: %clang -rtlib=compiler-rt -print-libgcc-file-name 2>&1 \
 // RUN:     --target=armv7m-none-eabi \
+// RUN:     --sysroot=%S/Inputs/resource_dir_with_arch_subdir \
 // RUN:     -resource-dir=%S/Inputs/resource_dir_with_arch_subdir \
 // RUN:   | FileCheck --check-prefix=CHECK-CLANGRT-ARM-BAREMETAL %s
 // CHECK-CLANGRT-ARM-BAREMETAL: libclang_rt.builtins-armv7m.a
Index: clang/test/Driver/baremetal.cpp
===================================================================
--- clang/test/Driver/baremetal.cpp
+++ clang/test/Driver/baremetal.cpp
@@ -118,9 +118,9 @@
 // Verify that the bare metal driver does not include any host system paths:
 // CHECK-AARCH64-NO-HOST-INC: InstalledDir: [[INSTALLEDDIR:.+]]
 // CHECK-AARCH64-NO-HOST-INC: "-resource-dir" "[[RESOURCE:[^"]+]]"
-// CHECK-AARCH64-NO-HOST-INC-SAME: "-internal-isystem" "[[INSTALLEDDIR]]{{[/\\]+}}..{{[/\\]+}}lib{{[/\\]+}}clang-runtimes{{[/\\]+}}aarch64-none-elf{{[/\\]+}}include{{[/\\]+}}c++{{[/\\]+}}v1"
+// CHECK-AARCH64-NO-HOST-INC-SAME: "-internal-isystem" "[[INSTALLEDDIR]]{{[/\\]+}}..{{[/\\]+}}lib{{[/\\]+}}clang-runtimes{{[/\\]+}}aarch64-none-elf{{[/\\]+.*}}include{{[/\\]+}}c++{{[/\\]+}}v1"
 // CHECK-AARCH64-NO-HOST-INC-SAME: "-internal-isystem" "[[RESOURCE]]{{[/\\]+}}include"
-// CHECK-AARCH64-NO-HOST-INC-SAME: "-internal-isystem" "[[INSTALLEDDIR]]{{[/\\]+}}..{{[/\\]+}}lib{{[/\\]+}}clang-runtimes{{[/\\]+}}aarch64-none-elf{{[/\\]+}}include"
+// CHECK-AARCH64-NO-HOST-INC-SAME: "-internal-isystem" "[[INSTALLEDDIR]]{{[/\\]+}}..{{[/\\]+}}lib{{[/\\]+}}clang-runtimes{{[/\\]+}}aarch64-none-elf{{[/\\]+.*}}include"
 
 // RUN: %clang %s -### --target=riscv64-unknown-elf -o %t.out -L some/directory/user/asked/for \
 // RUN:     --sysroot=%S/Inputs/basic_riscv64_tree/riscv64-unknown-elf 2>&1 \
Index: clang/test/Driver/baremetal-multilib.cpp
===================================================================
--- /dev/null
+++ clang/test/Driver/baremetal-multilib.cpp
@@ -0,0 +1,21 @@
+// REQUIRES: shell
+// UNSUPPORTED: system-windows
+
+// RUN: rm -rf %T/baremetal_multilib
+// RUN: mkdir -p %T/baremetal_multilib/bin
+// RUN: mkdir -p %T/baremetal_multilib/lib/clang-runtimes/arm-none-eabi/thumb/v8-m.main/fp/lib
+// RUN: touch %T/baremetal_multilib/lib/clang-runtimes/arm-none-eabi/thumb/v8-m.main/fp/lib/libclang_rt.builtins.a
+// RUN: ln -s %clang %T/baremetal_multilib/bin/clang
+// RUN: ln -s %S/Inputs/baremetal_multilib/arm-none-eabi/multilib.yaml %T/baremetal_multilib/lib/clang-runtimes/arm-none-eabi/multilib.yaml
+
+// RUN: %T/baremetal_multilib/bin/clang -no-canonical-prefixes %s -### -o %t.o 2>&1 \
+// RUN:     --target=thumbv8m.main-none-eabihf --sysroot= \
+// RUN:   | FileCheck --check-prefix=CHECK-V6M-C %s
+// CHECK-V6M-C: "{{.*}}clang{{.*}}" "-cc1" "-triple" "thumbv8m.main-none-unknown-eabihf"
+// CHECK-V6M-C-SAME: "-internal-isystem" "{{.*}}/baremetal_multilib/bin/../lib/clang-runtimes/arm-none-eabi/thumb/v8-m.main/fp/include/c++/v1"
+// CHECK-V6M-C-SAME: "-internal-isystem" "{{.*}}/baremetal_multilib/bin/../lib/clang-runtimes/arm-none-eabi/thumb/v8-m.main/fp/include"
+// CHECK-V6M-C-SAME: "-x" "c++" "{{.*}}/baremetal-multilib.cpp"
+// CHECK-V6M-C-NEXT: "{{[^"]*}}ld{{(\.(lld|bfd|gold))?}}{{(\.exe)?}}" "{{.*}}.o" "-Bstatic"
+// CHECK-V6M-C-SAME: "-L{{.*}}/baremetal_multilib/bin/../lib/clang-runtimes/arm-none-eabi/thumb/v8-m.main/fp/lib"
+// CHECK-V6M-C-SAME: "-lc" "-lm" "-lclang_rt.builtins"
+// CHECK-V6M-C-SAME: "-o" "{{.*}}.o"
Index: clang/test/Driver/arm-compiler-rt.c
===================================================================
--- clang/test/Driver/arm-compiler-rt.c
+++ clang/test/Driver/arm-compiler-rt.c
@@ -1,4 +1,5 @@
 // RUN: %clang -target arm-none-eabi \
+// RUN:     --sysroot=%S/Inputs/resource_dir_with_arch_subdir \
 // RUN:     -resource-dir=%S/Inputs/resource_dir_with_arch_subdir \
 // RUN:     -rtlib=compiler-rt -### %s 2>&1 \
 // RUN:   | FileCheck %s -check-prefix ARM-EABI
Index: clang/test/Driver/Inputs/baremetal_multilib/arm-none-eabi/multilib.yaml
===================================================================
--- /dev/null
+++ clang/test/Driver/Inputs/baremetal_multilib/arm-none-eabi/multilib.yaml
@@ -0,0 +1,101 @@
+# This file is in two parts:
+# 1. A list of library variants.
+# 2. A mapping from command line arguments to attributes.
+
+# How does clang use this file?
+# 1. If the ToolChain class for the architecture supports this form of
+#    multilibit then it loads file if present in sysroot.
+# 2. Generate clang -cc1 arguments for the user provided arguments.
+# 3. Compare the arguments against each regular expression and store
+#    associated attributes if there's a match.
+# 4. Find the last library variant whose attributes are a subset of the
+#    attributes derived from the user provided arguments.
+# 5. Use the path for the library variant as the sysroot.
+
+# The first section of the file is the list of library variants.
+# A library is considered compatible if the attributes (see below)
+# derived from its arguments are a subset of the attributes derived
+# from the arguments provided by the user.
+# If multiple libraries are deemed compatible then the one that appears
+# last in the list wins.
+
+variants:
+- path: thumb/v6-m/nofp
+  args: [--target=arm-none-eabi, -mfloat-abi=soft, -march=armv6m]
+  attrs: [thumb, soft, v6m]
+- path: thumb/v7-m/nofp
+  args: [--target=arm-none-eabi, -mfloat-abi=soft, -march=armv7m+nofp]
+  attrs: [thumb, soft, v7m]
+- path: thumb/v7e-m/nofp
+  args: [--target=arm-none-eabi, -mfloat-abi=soft, -march=armv7em, -mfpu=none]
+  attrs: [thumb, soft, v7em]
+- path: thumb/v8-m.main/nofp
+  args: [--target=arm-none-eabi, -mfloat-abi=soft, -march=armv8m.main+nofp]
+  attrs: [thumb, soft, v8m.main]
+- path: thumb/v8.1-m.main/nofp/nomve
+  args: [--target=arm-none-eabi, -mfloat-abi=soft, -march=armv8.1m.main+nofp+nomve]
+  attrs: [thumb, soft, v8.1m.main]
+- path: thumb/v7e-m/fpv4_sp_d16
+  args: [--target=arm-none-eabi, -mfloat-abi=hard, -march=armv7em, -mfpu=fpv4-sp-d16]
+  attrs: [thumb, hard, v7em, vfp2sp]
+- path: thumb/v7e-m/fpv5_d16
+  args: [--target=arm-none-eabi, -mfloat-abi=hard, -march=armv7em, -mfpu=fpv5-d16]
+  attrs: [thumb, hard, v7em, fp-armv8d16sp]
+- path: thumb/v8-m.main/fp
+  args: [--target=arm-none-eabi, -mfloat-abi=hard, -march=armv8m.main+fp]
+  attrs: [thumb, hard, v8m.main]
+- path: thumb/v8.1-m.main/fp
+  args: [--target=arm-none-eabi, -mfloat-abi=hard, -march=armv8.1m.main+fp]
+  attrs: [thumb, hard, v8.1m.main, vfp2sp]
+- path: thumb/v8.1-m.main/nofp/mve
+  args: [--target=arm-none-eabi, -mfloat-abi=hard, -march=armv8.1m.main+nofp+mve]
+  attrs: [thumb, hard, v8.1m.main, mve]
+
+
+# The second section of the file is a map from clang -cc1 command line
+# options to attributes. These are the options printed out if you run
+# clang with "-###"
+# The regex must match a whole logical argument string
+# e.g. "-target-cpu generic" or "-msoft-float".
+# "matchAttrs" attributes will be added if an argument matches, while
+# "noMatchAttrs" attributes will be added otherwise.
+arguments:
+- regex: -triple aarch64.*
+  matchAttrs: [aarch64]
+- regex: -triple thumb.*
+  matchAttrs: [thumb]
+- regex: -mfloat-abi soft
+  matchAttrs: [soft]
+- regex: -mfloat-abi hard
+  matchAttrs: [hard]
+# Floating point registers are enabled by default.
+# If the feature isn't disabled then we know it's enabled.
+- regex: -target-feature -fpregs
+  noMatchAttrs: [fpregs]
+- regex: -target-feature \+vfp2sp
+  matchAttrs: [vfp2sp]
+# Used to distinguish "-march=armv7em -mfpu=fpv4-sp-d16" and
+# "-march=armv7em -mfpu=fpv5-d16"
+- regex: -target-feature \+fp-armv8d16sp
+  matchAttrs: [fp-armv8d16sp]
+- regex: -target-feature \+mve
+  matchAttrs: [mve]
+- regex: -triple .*v6m-.*
+  matchAttrs: [v6m]
+- regex: -triple .*v7m-.*
+  matchAttrs: [v7m, v6m]
+- regex: -triple .*v7em-.*
+  matchAttrs: [v7em, v7m, v6m]
+- regex: -triple .*v8m\.base-.*
+  matchAttrs: [v8m.base, v6m]
+- regex: -triple .*v8m\.main-.*
+  matchAttrs: [v8m.main, v7em, v7m, v6m]
+# Match v8.1m and later. We assume that v8.2m (if/when it exists) will
+# be backwards compatible with v8.1m.
+# The alternative is to not recognise later versions, and require that
+# this multilib spec is updated before it can be used with newer
+# architecture versions.
+- regex: -triple .*v8\.[0-9]+m\.base-.*
+  matchAttrs: [v8m.base, v6m]
+- regex: -triple .*v8\.[0-9]+m\.main-.*
+  matchAttrs: [v8.1m.main, v8m.main, v7em, v7m, v6m]
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/Multilib2.h"
 #include "clang/Driver/Options.h"
 #include "llvm/Option/ArgList.h"
 #include "llvm/Support/Path.h"
@@ -28,6 +29,54 @@
 using namespace clang::driver::tools;
 using namespace clang::driver::toolchains;
 
+static std::string stringRemove(std::string S, StringRef Remove) {
+  for (;;) {
+    std::string::size_type i = S.find(Remove);
+    if (i == std::string::npos)
+      break;
+    S.erase(i, Remove.size());
+  }
+  return S;
+}
+
+/// To facilitate multilib, normalise the triple, discarding
+/// information that can be specified via other flags.
+static std::string simplifyTripleName(llvm::Triple Triple) {
+  Triple.setArchName(llvm::Triple::getArchTypePrefix(Triple.getArch()));
+
+  if (Triple.getEnvironment() == llvm::Triple::EABIHF) {
+    Triple.setEnvironment(llvm::Triple::EABI);
+  }
+
+  std::string Result = Triple.normalize();
+
+  return stringRemove(Result, "-unknown");
+}
+
+static std::string computeBaseSysRoot(const Driver &D, const llvm::Triple& Triple) {
+  if (!D.SysRoot.empty())
+    return D.SysRoot;
+
+  SmallString<128> SysRootDir(D.Dir);
+  llvm::sys::path::append(SysRootDir, "../lib/clang-runtimes");
+
+  // Try using the --target argument verbatim
+  SmallString<128> SysRootWithTarget(SysRootDir);
+  llvm::sys::path::append(SysRootWithTarget, D.getTargetTriple());
+  if (D.getVFS().exists(SysRootWithTarget)) {
+    return std::string(SysRootWithTarget);
+  }
+
+  SmallString<128> SysRootWithSimplifiedTarget(SysRootDir);
+  llvm::sys::path::append(SysRootWithSimplifiedTarget, simplifyTripleName(Triple));
+  if (D.getVFS().exists(SysRootWithSimplifiedTarget)) {
+    return std::string(SysRootWithSimplifiedTarget);
+  }
+
+  // Fall back to using the --target argument verbatim
+  return std::string(SysRootWithTarget);
+}
+
 static Multilib makeMultilib(StringRef commonSuffix) {
   return Multilib(commonSuffix, commonSuffix, commonSuffix);
 }
@@ -91,6 +140,62 @@
   return false;
 }
 
+static std::string makeMultilibFilePath(const Driver &D, const llvm::Triple &TargetTriple) {
+  const std::string BaseSysRoot = computeBaseSysRoot(D, TargetTriple);
+  llvm::SmallString<128> Result(BaseSysRoot);
+  llvm::sys::path::append(Result, "multilib.yaml");
+  return std::string(Result);
+}
+
+static bool readMultilibFile(const Driver &D, const llvm::Twine &MultilibFileName, Multilib2 &Multilibs) {
+  llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>> MB = D.getVFS().getBufferForFile(MultilibFileName);
+  return MB && Multilibs.parse(MB.get()->getBuffer(), llvm::errs());
+}
+
+/*
+Terms:
+* User sysroot: the sysroot specified by the user with --sysroot.
+* Base sysroot: the user sysroot, or a value chosen automatically according to
+  the target.
+* Final sysroot: if using multilib, a subdirectory of the base sysroot
+  containing include and lib directories; otherwise the same as the base
+  sysroot.
+* isysroot: Like sysroot, except for include directories.
+
+For each of lib (--sysroot) and include (-isysroot, falling back to --sysroot):
+1. Has the user specified a sysroot? If so, use that as the base sysroot
+   Else use try using the target verbatim as the base sysroot. Does it exist? If so, use it.
+   Else use the normalised, simplified target as the base sysroot.
+2. With the base sysroot chosen, multilib can be picked.
+   Does the base sysroot contain clang.multilib? If so, load it and use it.
+   Otherwise fall back to RISC-V's multilib implementation if appropriate,
+   otherwise no multilib.
+*/
+static bool findArmMultilibs(const Driver &D, const llvm::Triple &TargetTriple,
+                             const ArgList &Args,
+                             DetectedMultilibs &Result) {
+  Multilib2 M;
+
+  if (!readMultilibFile(D, makeMultilibFilePath(D, TargetTriple), M)) {
+    return false;
+  }
+
+  std::vector<std::string> ArgumentStrings;
+  for (const llvm::opt::Arg *A : Args) {
+    ArgumentStrings.push_back(A->getAsString(Args));
+  }
+
+  std::vector<const char *> ArgumentCStrings;
+  for (const std::string &S : ArgumentStrings) {
+    ArgumentCStrings.push_back(S.c_str());
+  }
+
+  std::pair<bool, std::string> SuccessAndPath = M.select(ArgumentCStrings);
+  StringRef Path = SuccessAndPath.second;
+  Result.SelectedMultilib = Multilib(Path, Path, Path);
+  return SuccessAndPath.first;
+}
+
 BareMetal::BareMetal(const Driver &D, const llvm::Triple &Triple,
                      const ArgList &Args)
     : ToolChain(D, Triple, Args) {
@@ -161,6 +266,11 @@
       SelectedMultilib = Result.SelectedMultilib;
       Multilibs = Result.Multilibs;
     }
+  } else if (isARMBareMetal(Triple) || isAArch64BareMetal(Triple)) {
+    if (findArmMultilibs(D, Triple, Args, Result)) {
+      SelectedMultilib = Result.SelectedMultilib;
+      Multilibs = Result.Multilibs;
+    }
   }
 }
 
@@ -174,15 +284,7 @@
 }
 
 std::string BareMetal::computeSysRoot() const {
-  if (!getDriver().SysRoot.empty())
-    return getDriver().SysRoot + SelectedMultilib.osSuffix();
-
-  SmallString<128> SysRootDir;
-  llvm::sys::path::append(SysRootDir, getDriver().Dir, "../lib/clang-runtimes",
-                          getDriver().getTargetTriple());
-
-  SysRootDir += SelectedMultilib.osSuffix();
-  return std::string(SysRootDir);
+  return computeBaseSysRoot(getDriver(), getTriple()) + SelectedMultilib.osSuffix();
 }
 
 void BareMetal::AddClangSystemIncludeArgs(const ArgList &DriverArgs,
Index: clang/lib/Driver/Multilib2.cpp
===================================================================
--- /dev/null
+++ clang/lib/Driver/Multilib2.cpp
@@ -0,0 +1,289 @@
+//===- Multilib2.cpp - Multilib2 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 "ToolChains/BareMetal.h"
+#include "ToolChains/Clang.h"
+#include "clang/Driver/Multilib2.h"
+#include "llvm/ADT/StringRef.h"
+#include "llvm/Support/raw_ostream.h"
+#include <algorithm>
+#include <cassert>
+#include <regex>
+#include <set>
+#include <vector>
+
+#include "clang/Driver/Compilation.h"
+#include "clang/Driver/Driver.h"
+#include "clang/Driver/DriverDiagnostic.h"
+#include "clang/Driver/Options.h"
+#include "llvm/Option/ArgList.h"
+#include "llvm/Support/Path.h"
+#include "llvm/Support/VirtualFileSystem.h"
+#include "llvm/Support/raw_ostream.h"
+#include "llvm/Support/SourceMgr.h"
+#include "llvm/Support/YAMLParser.h"
+#include "llvm/TargetParser/Host.h"
+
+using namespace clang;
+using namespace driver;
+
+Multilib2::Multilib2() {}
+
+bool Multilib2::parse(StringRef Input, raw_ostream &Err) {
+  std::vector<std::tuple<std::string, std::vector<std::string>, std::vector<std::string>>> MultilibList;
+  std::vector<std::tuple<std::string, std::vector<std::string>, std::vector<std::string>>> RegexAttributesList;
+
+
+  llvm::SourceMgr SM;
+  llvm::yaml::Stream Stream(Input, SM);
+  llvm::yaml::document_iterator DocumentIterator = Stream.begin();
+
+  // There has to be at least one document available.
+  if (DocumentIterator == Stream.end())
+    return false;
+
+  llvm::yaml::Node *DocumentRoot = DocumentIterator->getRoot();
+  if (!DocumentRoot) {
+    // TODO report error
+    return false;
+  }
+
+  auto *TopMappings = dyn_cast<llvm::yaml::MappingNode>(DocumentRoot);
+  if (!TopMappings) {
+    // TODO report error
+    return false;
+  }
+
+  for (auto &TopMapping : *TopMappings) {
+    // The keys should be strings, which represent a source-file path.
+    auto *TopKey = dyn_cast<llvm::yaml::ScalarNode>(TopMapping.getKey());
+    if (!TopKey) {
+      // TODO report error
+      return false;
+    }
+
+    if (TopKey->getRawValue() == "variants") {
+      auto Variants = dyn_cast<llvm::yaml::SequenceNode>(TopMapping.getValue());
+      if (!Variants) {
+        // TODO report error
+        return false;
+      }
+      for (auto &Variant : *Variants) {
+        std::string Path;
+        std::vector<std::string> Args, Attrs;
+
+        llvm::yaml::MappingNode *VariantMappings = dyn_cast<llvm::yaml::MappingNode>(&Variant);
+        if (!VariantMappings) {
+          // TODO report error
+          return false;
+        }
+        for (auto &VariantMapping : *VariantMappings) {
+          auto *VariantKey = dyn_cast<llvm::yaml::ScalarNode>(VariantMapping.getKey());
+          if (!VariantKey) {
+            // TODO report error
+            return false;
+          }
+          if (VariantKey->getRawValue() == "path") {
+            auto *VariantValue = dyn_cast<llvm::yaml::ScalarNode>(VariantMapping.getValue());
+            if (!VariantValue) {
+              // TODO report error
+              return false;
+            }
+            SmallString<32> PathStorage;
+            Path = VariantValue->getValue(PathStorage).str();
+          }
+          if (VariantKey->getRawValue() == "args") {
+            auto Arguments = dyn_cast<llvm::yaml::SequenceNode>(VariantMapping.getValue());
+            if (!Arguments) {
+              // TODO report error
+              return false;
+            }
+            for (auto &Argument : *Arguments) {
+              auto *ArgumentValue = dyn_cast<llvm::yaml::ScalarNode>(&Argument);
+              if (!ArgumentValue) {
+                // TODO report error
+                return false;
+              }
+              SmallString<32> ArgumentStorage;
+              Args.push_back(ArgumentValue->getValue(ArgumentStorage).str());
+            }
+          }
+          if (VariantKey->getRawValue() == "attrs") {
+            auto Attributes = dyn_cast<llvm::yaml::SequenceNode>(VariantMapping.getValue());
+            if (!Attributes) {
+              // TODO report error
+              return false;
+            }
+            for (auto &Attribute : *Attributes) {
+              auto *AttributeValue = dyn_cast<llvm::yaml::ScalarNode>(&Attribute);
+              if (!AttributeValue) {
+                // TODO report error
+                return false;
+              }
+              SmallString<32> AttributeStorage;
+              Attrs.push_back(AttributeValue->getValue(AttributeStorage).str());
+            }
+          }
+        }
+        if (StringRef(Path).starts_with("/")) {
+          Err << "paths must be relative. \"" << Path << "\" starts with \"/\"\n";
+          return false;
+        }
+        if (Path == ".") {
+          Path = "";
+        } else {
+          Path = "/" + Path;
+        }
+        // TODO: add test that breaks if no sorting is done.
+        std::sort(Attrs.begin(), Attrs.end());
+        MultilibList.push_back(std::tuple<std::string, std::vector<std::string>, std::vector<std::string>>(Path, Args, Attrs));
+      }
+    } else if (TopKey->getRawValue() == "arguments") {
+      auto Arguments = dyn_cast<llvm::yaml::SequenceNode>(TopMapping.getValue());
+      if (!Arguments) {
+        // TODO report error
+        return false;
+      }
+      for (auto &Argument : *Arguments) {
+        std::string Regex;
+        std::vector<std::string> MatchAttrs, NoMatchAttrs;
+
+        llvm::yaml::MappingNode *ArgumentMappings = dyn_cast<llvm::yaml::MappingNode>(&Argument);
+        if (!ArgumentMappings) {
+          // TODO report error
+          return false;
+        }
+        for (auto &ArgumentMapping : *ArgumentMappings) {
+          auto *ArgumentKey = dyn_cast<llvm::yaml::ScalarNode>(ArgumentMapping.getKey());
+          if (!ArgumentKey) {
+            // TODO report error
+            return false;
+          }
+          if (ArgumentKey->getRawValue() == "regex") {
+            auto *ArgumentValue = dyn_cast<llvm::yaml::ScalarNode>(ArgumentMapping.getValue());
+            if (!ArgumentValue) {
+              // TODO report error
+              return false;
+            }
+            SmallString<32> PathStorage;
+            Regex = ArgumentValue->getValue(PathStorage).str();
+          }
+          if (ArgumentKey->getRawValue() == "matchAttrs") {
+            auto Arguments = dyn_cast<llvm::yaml::SequenceNode>(ArgumentMapping.getValue());
+            if (!Arguments) {
+              // TODO report error
+              return false;
+            }
+            for (auto &Argument : *Arguments) {
+              auto *ArgumentValue = dyn_cast<llvm::yaml::ScalarNode>(&Argument);
+              if (!ArgumentValue) {
+                // TODO report error
+                return false;
+              }
+              SmallString<32> ArgumentStorage;
+              MatchAttrs.push_back(ArgumentValue->getValue(ArgumentStorage).str());
+            }
+          }
+          if (ArgumentKey->getRawValue() == "noMatchAttrs") {
+            auto Attributes = dyn_cast<llvm::yaml::SequenceNode>(ArgumentMapping.getValue());
+            if (!Attributes) {
+              // TODO report error
+              return false;
+            }
+            for (auto &Attribute : *Attributes) {
+              auto *AttributeValue = dyn_cast<llvm::yaml::ScalarNode>(&Attribute);
+              if (!AttributeValue) {
+                // TODO report error
+                return false;
+              }
+              SmallString<32> AttributeStorage;
+              NoMatchAttrs.push_back(AttributeValue->getValue(AttributeStorage).str());
+            }
+          }
+        }
+        RegexAttributesList.push_back(std::tuple<std::string, std::vector<std::string>, std::vector<std::string>>(Regex, MatchAttrs, NoMatchAttrs));
+      }
+    }
+  }
+
+  Multilibs.swap(MultilibList);
+  RegexAttributes.swap(RegexAttributesList);
+
+  return true;
+}
+
+static std::vector<std::string> normalizeArgs(ArrayRef<const char *> ArgsWithoutFile) {
+  const char *ExecutableName = "/no/such/clang";
+  const char *TestFileName = "test.c";
+  std::vector<const char *> Args = {ExecutableName, TestFileName};
+  for (const char *A : ArgsWithoutFile) {
+    if (A != StringRef("-###")) {
+      Args.push_back(A);
+    }
+  }
+  IntrusiveRefCntPtr<DiagnosticOptions> DiagOpts = new DiagnosticOptions();
+  IntrusiveRefCntPtr<DiagnosticIDs> DiagID(new DiagnosticIDs());
+  struct TestDiagnosticConsumer : public DiagnosticConsumer {};
+  IntrusiveRefCntPtr<llvm::vfs::InMemoryFileSystem> InMemoryFileSystem(
+      new llvm::vfs::InMemoryFileSystem);
+  InMemoryFileSystem->addFile(TestFileName, time_t(), llvm::MemoryBuffer::getMemBuffer(""));
+  DiagnosticsEngine Diags(DiagID, &*DiagOpts, new TestDiagnosticConsumer);
+  Driver D(ExecutableName, llvm::sys::getDefaultTargetTriple(), Diags, "clang LLVM compiler", InMemoryFileSystem);
+  std::unique_ptr<Compilation> C(D.BuildCompilation(Args));
+  if (C->getJobs().empty()) {
+    return std::vector<std::string>();
+  }
+  llvm::opt::ArgStringList NormalisedArgStrings = C->getJobs().begin()->getArguments();
+
+  unsigned MissingArgIndex = 0, MissingArgCount = 0;
+  llvm::opt::InputArgList ParsedArgs = getDriverOptTable().ParseArgs(NormalisedArgStrings, MissingArgIndex, MissingArgCount);
+
+  std::vector<std::string> Result;
+  for (const llvm::opt::Arg *A : ParsedArgs) {
+    Result.push_back(A->getAsString(ParsedArgs));
+  }
+  return Result;
+}
+
+static std::vector<std::string> argsToAttributes(ArrayRef<std::string> Arguments, const std::vector<std::tuple<std::string, std::vector<std::string>, std::vector<std::string>>> &RegexAttributesList) {
+  std::set<std::string> Result;
+  for (const std::tuple<std::string, std::vector<std::string>, std::vector<std::string>> &RegexAttributes : RegexAttributesList) {
+    const std::regex Regex(std::get<0>(RegexAttributes));
+    const std::vector<std::string> &MatchAttrs = std::get<1>(RegexAttributes);
+    const std::vector<std::string> &NoMatchAttrs = std::get<2>(RegexAttributes);
+    bool Match = false;
+    for (const std::string &Argument : Arguments) {
+      if (std::regex_match(Argument, Regex)) {
+        Match = true;
+        break;
+      }
+    }
+    if (Match) {
+      Result.insert(MatchAttrs.begin(), MatchAttrs.end());
+    } else {
+      Result.insert(NoMatchAttrs.begin(), NoMatchAttrs.end());
+    }
+  }
+  return std::vector<std::string>(Result.begin(), Result.end());
+}
+
+std::pair<bool, std::string> Multilib2::select(ArrayRef<const char *> Arguments) const {
+  std::pair<bool, std::string> Result(false, "");
+  std::vector<std::string> NormalizedArguments(normalizeArgs(Arguments));
+  if (NormalizedArguments.empty()) {
+    return std::pair<bool, std::string>(false, "");
+  }
+  std::vector<std::string> Attrs = argsToAttributes(NormalizedArguments, RegexAttributes);
+  for (const std::tuple<std::string, std::vector<std::string>, std::vector<std::string>> &M : Multilibs) {
+    const std::vector<std::string> &MAttrs = std::get<2>(M);
+    if (std::includes(Attrs.begin(), Attrs.end(), MAttrs.begin(), MAttrs.end())) {
+      Result = std::pair<bool, std::string>(true, std::get<0>(M));
+    }
+  }
+  return Result;
+}
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
+  Multilib2.cpp
   OffloadBundler.cpp
   OptionUtils.cpp
   Phases.cpp
Index: clang/include/clang/Driver/Multilib2.h
===================================================================
--- /dev/null
+++ clang/include/clang/Driver/Multilib2.h
@@ -0,0 +1,40 @@
+//===- Multilib2.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_MULTILIB2_H
+#define LLVM_CLANG_DRIVER_MULTILIB2_H
+
+#include "clang/Basic/LLVM.h"
+#include <string>
+
+namespace clang {
+namespace driver {
+
+class Multilib2 {
+public:
+  using const_iterator = std::vector<std::tuple<std::string, std::vector<std::string>, std::vector<std::string>>>::const_iterator;
+
+  Multilib2();
+
+  size_t size() const { return Multilibs.size(); }
+  const_iterator begin() const { return Multilibs.begin(); }
+  const_iterator end() const { return Multilibs.end(); }
+
+  bool parse(StringRef, raw_ostream &Err);
+
+  std::pair<bool, std::string> select(ArrayRef<const char *> Arguments) const;
+
+private:
+  std::vector<std::tuple<std::string, std::vector<std::string>, std::vector<std::string>>> Multilibs;
+  std::vector<std::tuple<std::string, std::vector<std::string>, std::vector<std::string>>> RegexAttributes;
+};
+
+} // namespace driver
+} // namespace clang
+
+#endif // LLVM_CLANG_DRIVER_MULTILIB2_H
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to