[clang] [RISCV] Implement multi-lib reuse rule for RISC-V bare-metal toolchain (PR #73765)

Brandon Wu via cfe-commits cfe-commits at lists.llvm.org
Mon Dec 4 00:35:16 PST 2023


https://github.com/4vtomat updated https://github.com/llvm/llvm-project/pull/73765

>From 0c5f7497a8ce41695cba8f16a9402d74e4f798ea Mon Sep 17 00:00:00 2001
From: Brandon Wu <brandon.wu at sifive.com>
Date: Wed, 29 Nov 2023 00:27:25 -0800
Subject: [PATCH 1/2] [RISCV] Implement multi-lib reuse rule for RISC-V
 bare-metal toolchain

Extend the multi-lib re-use selection mechanism for RISC-V.
This function will try to re-use multi-lib if they are compatible.
Definition of compatible:
  - ABI must be the same.
  - multi-lib is a subset of current arch, e.g. multi-lib=march=rv32im
    is a subset of march=rv32imc.
  - march that contains atomic extension can't reuse multi-lib that
    doesn't have atomic, vice versa. e.g. multi-lib=march=rv32im and
    march=rv32ima are not compatible, because software and hardware
    atomic operation can't work together correctly.
---
 clang/lib/Driver/ToolChains/Gnu.cpp           | 127 +++++++++++++++++-
 .../riscv-toolchain-gcc-multilib-reuse.c      |  86 ++++++++++++
 2 files changed, 212 insertions(+), 1 deletion(-)
 create mode 100644 clang/test/Driver/riscv-toolchain-gcc-multilib-reuse.c

diff --git a/clang/lib/Driver/ToolChains/Gnu.cpp b/clang/lib/Driver/ToolChains/Gnu.cpp
index 16cf570722726..5bb09101fef00 100644
--- a/clang/lib/Driver/ToolChains/Gnu.cpp
+++ b/clang/lib/Driver/ToolChains/Gnu.cpp
@@ -30,6 +30,7 @@
 #include "llvm/Option/ArgList.h"
 #include "llvm/Support/CodeGen.h"
 #include "llvm/Support/Path.h"
+#include "llvm/Support/RISCVISAInfo.h"
 #include "llvm/Support/VirtualFileSystem.h"
 #include "llvm/TargetParser/TargetParser.h"
 #include <system_error>
@@ -1715,6 +1716,129 @@ static void findCSKYMultilibs(const Driver &D, const llvm::Triple &TargetTriple,
     Result.Multilibs = CSKYMultilibs;
 }
 
+/// Extend the multi-lib re-use selection mechanism for RISC-V.
+/// This funciton will try to re-use multi-lib if they are compatible.
+/// Definition of compatible:
+///   - ABI must be the same.
+///   - multi-lib is a subset of current arch, e.g. multi-lib=march=rv32im
+///     is a subset of march=rv32imc.
+///   - march that contains atomic extension can't reuse multi-lib that
+///     doesn't has atomic, vice versa. e.g. multi-lib=march=rv32im and
+///     march=rv32ima are not compatible, because software and hardware
+///     atomic operation can't work together correctly.
+static bool
+RISCVMultilibSelect(const MultilibSet &RISCVMultilibSet, StringRef Arch,
+                    const Multilib::flags_list &Flags,
+                    llvm::SmallVector<Multilib> &SelectedMultilibs) {
+  // Try to find the perfect matching multi-lib first.
+  if (RISCVMultilibSet.select(Flags, SelectedMultilibs))
+    return true;
+
+  llvm::StringMap<bool> FlagSet;
+  Multilib::flags_list NewFlags;
+  std::vector<MultilibBuilder> NewMultilibs;
+
+  auto ParseResult = llvm::RISCVISAInfo::parseArchString(
+      Arch, /*EnableExperimentalExtension=*/true,
+      /*ExperimentalExtensionVersionCheck=*/false);
+  if (!ParseResult) {
+    // Ignore any error here, we assume it will handled in another place.
+    consumeError(ParseResult.takeError());
+    return false;
+  }
+  auto &ISAInfo = *ParseResult;
+
+  auto CurrentExts = ISAInfo->getExtensions();
+
+  addMultilibFlag(ISAInfo->getXLen() == 32, "-m32", NewFlags);
+  addMultilibFlag(ISAInfo->getXLen() == 64, "-m64", NewFlags);
+
+  // Collect all flags except march=*
+  for (StringRef Flag : Flags) {
+    if (Flag.startswith("!march=") || Flag.startswith("-march="))
+      continue;
+
+    NewFlags.push_back(Flag.str());
+  }
+
+  llvm::StringSet<> AllArchExts;
+  // Reconstruct multi-lib list, and break march option into seperated
+  // extension. e.g. march=rv32im -> +i +m
+  for (auto M : RISCVMultilibSet) {
+    bool Skip = false;
+
+    MultilibBuilder NewMultilib =
+        MultilibBuilder(M.gccSuffix(), M.osSuffix(), M.includeSuffix());
+    for (StringRef Flag : M.flags()) {
+      // Add back the all option except -march.
+      if (!Flag.startswith("-march=")) {
+        NewMultilib.flag(Flag);
+        continue;
+      }
+
+      // Break down -march into individual extension.
+      auto MLConfigParseResult = llvm::RISCVISAInfo::parseArchString(
+          Flag.drop_front(7), /*EnableExperimentalExtension=*/true,
+          /*ExperimentalExtensionVersionCheck=*/false);
+      if (!MLConfigParseResult) {
+        // Ignore any error here, we assume it will handled in another place.
+        llvm::consumeError(MLConfigParseResult.takeError());
+
+        // We might got parsing error if rv32e in the list, we could just skip
+        // that and process the rest of multi-lib configs.
+        Skip = true;
+        continue;
+      }
+      auto &MLConfigISAInfo = *MLConfigParseResult;
+
+      auto MLConfigArchExts = MLConfigISAInfo->getExtensions();
+      for (auto MLConfigArchExt : MLConfigArchExts) {
+        auto ExtName = MLConfigArchExt.first;
+        NewMultilib.flag(Twine("-", ExtName).str());
+
+        if (!AllArchExts.contains(ExtName)) {
+          AllArchExts.insert(ExtName);
+          addMultilibFlag(ISAInfo->hasExtension(ExtName),
+                          Twine("-", ExtName).str(), NewFlags);
+        }
+      }
+
+      // Check the XLEN explicitly.
+      if (MLConfigISAInfo->getXLen() == 32) {
+        NewMultilib.flag("-m32");
+        NewMultilib.flag("!m64");
+      } else {
+        NewMultilib.flag("!m32");
+        NewMultilib.flag("-m64");
+      }
+
+      // Atomic extension must be explicitly checked, soft and hard atomic
+      // operation never co-work correctly.
+      if (!MLConfigISAInfo->hasExtension("a"))
+        NewMultilib.flag("!a");
+    }
+
+    if (Skip)
+      continue;
+
+    NewMultilibs.emplace_back(NewMultilib);
+  }
+
+  // Build an internal used only multi-lib list, used for checking any
+  // compatible multi-lib.
+  MultilibSet NewRISCVMultilibs =
+      MultilibSetBuilder().Either(NewMultilibs).makeMultilibSet();
+
+  if (NewRISCVMultilibs.select(NewFlags, SelectedMultilibs))
+    for (const Multilib &NewSelectedM : SelectedMultilibs)
+      for (auto M : RISCVMultilibSet)
+        // Look up the corresponding multi-lib entry in original multi-lib set.
+        if (M.gccSuffix() == NewSelectedM.gccSuffix())
+          return true;
+
+  return false;
+}
+
 static void findRISCVBareMetalMultilibs(const Driver &D,
                                         const llvm::Triple &TargetTriple,
                                         StringRef Path, const ArgList &Args,
@@ -1766,7 +1890,8 @@ static void findRISCVBareMetalMultilibs(const Driver &D,
     }
   }
 
-  if (RISCVMultilibs.select(Flags, Result.SelectedMultilibs))
+  if (RISCVMultilibSelect(RISCVMultilibs, MArch, Flags,
+                          Result.SelectedMultilibs))
     Result.Multilibs = RISCVMultilibs;
 }
 
diff --git a/clang/test/Driver/riscv-toolchain-gcc-multilib-reuse.c b/clang/test/Driver/riscv-toolchain-gcc-multilib-reuse.c
new file mode 100644
index 0000000000000..65afe82931de5
--- /dev/null
+++ b/clang/test/Driver/riscv-toolchain-gcc-multilib-reuse.c
@@ -0,0 +1,86 @@
+// Test case for scanning input of GCC output as multilib config
+// Skip this test on Windows, we can't create a dummy GCC to output
+// multilib config, ExecuteAndWait only execute *.exe file.
+// UNSUPPORTED: system-windows
+
+// RUN: %clang %s \
+// RUN:   -target riscv64-unknown-elf \
+// RUN:   --gcc-toolchain=%S/Inputs/multilib_riscv_elf_sdk \
+// RUN:   --print-multi-directory \
+// RUN:   -march=rv32imc -mabi=ilp32 \
+// RUN:   | FileCheck -check-prefix=GCC-MULTI-LIB-REUSE-RV32IMC-ILP32 %s
+// GCC-MULTI-LIB-REUSE-RV32IMC-ILP32: rv32im/ilp32
+// GCC-MULTI-LIB-REUSE-RV32IMC-ILP32-NOT:  {{^.+$}}
+
+// Check rv32imac won't reuse rv32im or rv32ic
+// RUN: %clang %s \
+// RUN:   -target riscv64-unknown-elf \
+// RUN:   --gcc-toolchain=%S/Inputs/multilib_riscv_elf_sdk \
+// RUN:   --print-multi-directory \
+// RUN:   -march=rv32imac -mabi=ilp32 \
+// RUN:   | FileCheck -check-prefix=GCC-MULTI-LIB-REUSE-RV32IMAC-ILP32 %s
+// GCC-MULTI-LIB-REUSE-RV32IMAC-ILP32: rv32imac/ilp32
+// GCC-MULTI-LIB-REUSE-RV32IMAC-ILP32--NOT: {{^.+$}}
+
+// RUN: %clang %s \
+// RUN:   -target riscv64-unknown-elf \
+// RUN:   --gcc-toolchain=%S/Inputs/multilib_riscv_elf_sdk \
+// RUN:   --print-multi-directory \
+// RUN:   -march=rv32iac -mabi=ilp32 \
+// RUN:   | FileCheck -check-prefix=GCC-MULTI-LIB-REUSE-RV32IAC-ILP32 %s
+// GCC-MULTI-LIB-REUSE-RV32IAC-ILP32: rv32iac/ilp32
+// GCC-MULTI-LIB-REUSE-RV32IAC-ILP32-NOT: {{^.+$}}
+
+// RUN: %clang %s \
+// RUN:   -target riscv64-unknown-elf \
+// RUN:   --gcc-toolchain=%S/Inputs/multilib_riscv_elf_sdk \
+// RUN:   --print-multi-directory \
+// RUN:   -march=rv32imafdc -mabi=ilp32f \
+// RUN:   | FileCheck -check-prefix=GCC-MULTI-LIB-REUSE-RV32IMAFDC-ILP32F %s
+// GCC-MULTI-LIB-REUSE-RV32IMAFDC-ILP32F: rv32imafc/ilp32f
+// GCC-MULTI-LIB-REUSE-RV32IMAFDC-ILP32F-NOT: {{^.+$}}
+
+// RUN: %clang %s \
+// RUN:   -target riscv64-unknown-elf \
+// RUN:   --gcc-toolchain=%S/Inputs/multilib_riscv_elf_sdk \
+// RUN:   --print-multi-directory \
+// RUN:   -march=rv32imafdc -mabi=ilp32d \
+// RUN:   | FileCheck -check-prefix=GCC-MULTI-LIB-REUSE-RV32IMAFDC-ILP32D %s
+// GCC-MULTI-LIB-REUSE-RV32IMAFDC-ILP32D: .
+// GCC-MULTI-LIB-REUSE-RV32IMAFDC-ILP32D-NOT: {{^.+$}}
+
+// RUN: %clang %s \
+// RUN:   -target riscv64-unknown-elf \
+// RUN:   --gcc-toolchain=%S/Inputs/multilib_riscv_elf_sdk \
+// RUN:   --print-multi-directory \
+// RUN:   -march=rv64imafc -mabi=lp64 \
+// RUN:   | FileCheck -check-prefix=GCC-MULTI-LIB-REUSE-RV64IMAFC-LP64 %s
+// GCC-MULTI-LIB-REUSE-RV64IMAFC-LP64: rv64imac/lp64
+// GCC-MULTI-LIB-REUSE-RV64IMAFC-LP64-NOT: {{^.+$}}
+
+// RUN: %clang %s \
+// RUN:   -target riscv64-unknown-elf \
+// RUN:   --gcc-toolchain=%S/Inputs/multilib_riscv_elf_sdk \
+// RUN:   --print-multi-directory \
+// RUN:   -march=rv32imafc_zfh -mabi=ilp32 \
+// RUN:   | FileCheck -check-prefix=GCC-MULTI-LIB-REUSE-RV32IMAFC_ZFH-ILP32 %s
+// GCC-MULTI-LIB-REUSE-RV32IMAFC_ZFH-ILP32: rv32imac/ilp32
+// GCC-MULTI-LIB-REUSE-RV32IMAFC_ZFH-ILP32-NOT: {{^.+$}}
+
+// RUN: %clang %s \
+// RUN:   -target riscv64-unknown-elf \
+// RUN:   --gcc-toolchain=%S/Inputs/multilib_riscv_elf_sdk \
+// RUN:   --print-multi-directory \
+// RUN:   -march=rv32i_zvkb -mabi=ilp32 \
+// RUN:   | FileCheck -check-prefix=GCC-MULTI-LIB-REUSE-RV32I_ZVKB-ILP32 %s
+// GCC-MULTI-LIB-REUSE-RV32I_ZVKB-ILP32: rv32i/ilp32
+// GCC-MULTI-LIB-REUSE-RV32I_ZVKB-ILP32-NOT: {{^.+$}}
+
+// RUN: %clang %s \
+// RUN:   -target riscv64-unknown-elf \
+// RUN:   --gcc-toolchain=%S/Inputs/multilib_riscv_elf_sdk \
+// RUN:   --print-multi-directory \
+// RUN:   -march=rv64imfc -mabi=lp64 \
+// RUN:   | FileCheck -check-prefix=GCC-MULTI-LIB-REUSE-RV64IMFC-LP64 %s
+// GCC-MULTI-LIB-REUSE-RV64IMFC-LP64: .
+// GCC-MULTI-LIB-REUSE-RV64IMFC-LP64-NOT: {{^.+$}}

>From fb09422283ac1456fe008ef5cf2662571d8256b9 Mon Sep 17 00:00:00 2001
From: Brandon Wu <brandon.wu at sifive.com>
Date: Mon, 4 Dec 2023 00:15:33 -0800
Subject: [PATCH 2/2] fixup! [RISCV] Implement multi-lib reuse rule for RISC-V
 bare-metal toolchain

---
 clang/lib/Driver/ToolChains/Gnu.cpp           | 31 ++++++++++---------
 .../riscv-toolchain-gcc-multilib-reuse.c      |  5 ---
 2 files changed, 16 insertions(+), 20 deletions(-)

diff --git a/clang/lib/Driver/ToolChains/Gnu.cpp b/clang/lib/Driver/ToolChains/Gnu.cpp
index 5bb09101fef00..3333ac7c28aea 100644
--- a/clang/lib/Driver/ToolChains/Gnu.cpp
+++ b/clang/lib/Driver/ToolChains/Gnu.cpp
@@ -1717,13 +1717,13 @@ static void findCSKYMultilibs(const Driver &D, const llvm::Triple &TargetTriple,
 }
 
 /// Extend the multi-lib re-use selection mechanism for RISC-V.
-/// This funciton will try to re-use multi-lib if they are compatible.
+/// This function will try to re-use multi-lib if they are compatible.
 /// Definition of compatible:
 ///   - ABI must be the same.
 ///   - multi-lib is a subset of current arch, e.g. multi-lib=march=rv32im
 ///     is a subset of march=rv32imc.
 ///   - march that contains atomic extension can't reuse multi-lib that
-///     doesn't has atomic, vice versa. e.g. multi-lib=march=rv32im and
+///     doesn't have atomic, vice versa. e.g. multi-lib=march=rv32im and
 ///     march=rv32ima are not compatible, because software and hardware
 ///     atomic operation can't work together correctly.
 static bool
@@ -1734,21 +1734,20 @@ RISCVMultilibSelect(const MultilibSet &RISCVMultilibSet, StringRef Arch,
   if (RISCVMultilibSet.select(Flags, SelectedMultilibs))
     return true;
 
-  llvm::StringMap<bool> FlagSet;
   Multilib::flags_list NewFlags;
   std::vector<MultilibBuilder> NewMultilibs;
 
-  auto ParseResult = llvm::RISCVISAInfo::parseArchString(
-      Arch, /*EnableExperimentalExtension=*/true,
-      /*ExperimentalExtensionVersionCheck=*/false);
+  llvm::Expected<std::unique_ptr<llvm::RISCVISAInfo>> ParseResult =
+      llvm::RISCVISAInfo::parseArchString(
+          Arch, /*EnableExperimentalExtension=*/true,
+          /*ExperimentalExtensionVersionCheck=*/false);
   if (!ParseResult) {
-    // Ignore any error here, we assume it will handled in another place.
+    // Ignore any error here, we assume it will be handled in another place.
     consumeError(ParseResult.takeError());
     return false;
   }
-  auto &ISAInfo = *ParseResult;
 
-  auto CurrentExts = ISAInfo->getExtensions();
+  auto &ISAInfo = *ParseResult;
 
   addMultilibFlag(ISAInfo->getXLen() == 32, "-m32", NewFlags);
   addMultilibFlag(ISAInfo->getXLen() == 64, "-m64", NewFlags);
@@ -1762,7 +1761,7 @@ RISCVMultilibSelect(const MultilibSet &RISCVMultilibSet, StringRef Arch,
   }
 
   llvm::StringSet<> AllArchExts;
-  // Reconstruct multi-lib list, and break march option into seperated
+  // Reconstruct multi-lib list, and break march option into separated
   // extension. e.g. march=rv32im -> +i +m
   for (auto M : RISCVMultilibSet) {
     bool Skip = false;
@@ -1777,21 +1776,23 @@ RISCVMultilibSelect(const MultilibSet &RISCVMultilibSet, StringRef Arch,
       }
 
       // Break down -march into individual extension.
-      auto MLConfigParseResult = llvm::RISCVISAInfo::parseArchString(
-          Flag.drop_front(7), /*EnableExperimentalExtension=*/true,
-          /*ExperimentalExtensionVersionCheck=*/false);
+      llvm::Expected<std::unique_ptr<llvm::RISCVISAInfo>> MLConfigParseResult =
+          llvm::RISCVISAInfo::parseArchString(
+              Flag.drop_front(7), /*EnableExperimentalExtension=*/true,
+              /*ExperimentalExtensionVersionCheck=*/false);
       if (!MLConfigParseResult) {
         // Ignore any error here, we assume it will handled in another place.
         llvm::consumeError(MLConfigParseResult.takeError());
 
-        // We might got parsing error if rv32e in the list, we could just skip
+        // We might get parsing error if rv32e in the list, we could just skip
         // that and process the rest of multi-lib configs.
         Skip = true;
         continue;
       }
       auto &MLConfigISAInfo = *MLConfigParseResult;
 
-      auto MLConfigArchExts = MLConfigISAInfo->getExtensions();
+      const llvm::RISCVISAInfo::OrderedExtensionMap &MLConfigArchExts =
+          MLConfigISAInfo->getExtensions();
       for (auto MLConfigArchExt : MLConfigArchExts) {
         auto ExtName = MLConfigArchExt.first;
         NewMultilib.flag(Twine("-", ExtName).str());
diff --git a/clang/test/Driver/riscv-toolchain-gcc-multilib-reuse.c b/clang/test/Driver/riscv-toolchain-gcc-multilib-reuse.c
index 65afe82931de5..1f8a5a8821edf 100644
--- a/clang/test/Driver/riscv-toolchain-gcc-multilib-reuse.c
+++ b/clang/test/Driver/riscv-toolchain-gcc-multilib-reuse.c
@@ -1,8 +1,3 @@
-// Test case for scanning input of GCC output as multilib config
-// Skip this test on Windows, we can't create a dummy GCC to output
-// multilib config, ExecuteAndWait only execute *.exe file.
-// UNSUPPORTED: system-windows
-
 // RUN: %clang %s \
 // RUN:   -target riscv64-unknown-elf \
 // RUN:   --gcc-toolchain=%S/Inputs/multilib_riscv_elf_sdk \



More information about the cfe-commits mailing list