[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
Wed Nov 29 00:43:27 PST 2023


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

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.


>From 4f6500b9cc359522e399ef0965f01ff820d7cd54 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] [RISCV] Implement multi-lib reuse rule for RISC-V bare-metal
 toolchain

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.
---
 clang/lib/Driver/ToolChains/Gnu.cpp           | 134 +++++++++++++++++-
 .../8.2.0/rv32ic/ilp32/crtbegin.o             |   0
 .../8.2.0/rv32if/ilp32f/crtbegin.o            |   0
 .../8.2.0/rv32ifc/ilp32/crtbegin.o            |   0
 .../8.2.0/rv32ifc/ilp32f/crtbegin.o           |   0
 .../riscv-toolchain-gcc-multilib-reuse.c      |  86 +++++++++++
 6 files changed, 216 insertions(+), 4 deletions(-)
 create mode 100644 clang/test/Driver/Inputs/multilib_riscv_elf_sdk/lib/gcc/riscv64-unknown-elf/8.2.0/rv32ic/ilp32/crtbegin.o
 create mode 100644 clang/test/Driver/Inputs/multilib_riscv_elf_sdk/lib/gcc/riscv64-unknown-elf/8.2.0/rv32if/ilp32f/crtbegin.o
 create mode 100644 clang/test/Driver/Inputs/multilib_riscv_elf_sdk/lib/gcc/riscv64-unknown-elf/8.2.0/rv32ifc/ilp32/crtbegin.o
 create mode 100644 clang/test/Driver/Inputs/multilib_riscv_elf_sdk/lib/gcc/riscv64-unknown-elf/8.2.0/rv32ifc/ilp32f/crtbegin.o
 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 16cf5707227264d..ffc53377d97bd02 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,
@@ -1727,9 +1851,10 @@ static void findRISCVBareMetalMultilibs(const Driver &D,
   // currently only support the set of multilibs like riscv-gnu-toolchain does.
   // TODO: support MULTILIB_REUSE
   constexpr RiscvMultilib RISCVMultilibSet[] = {
-      {"rv32i", "ilp32"},     {"rv32im", "ilp32"},     {"rv32iac", "ilp32"},
-      {"rv32imac", "ilp32"},  {"rv32imafc", "ilp32f"}, {"rv64imac", "lp64"},
-      {"rv64imafdc", "lp64d"}};
+      {"rv32i", "ilp32"},    {"rv32ic", "ilp32"},     {"rv32if", "ilp32f"},
+      {"rv32im", "ilp32"},   {"rv32iac", "ilp32"},    {"rv32ifc", "ilp32"},
+      {"rv32imac", "ilp32"}, {"rv32imafc", "ilp32f"}, {"rv64imac", "lp64"},
+      {"rv64imac", "lp64"},  {"rv64imafdc", "lp64d"}};
 
   std::vector<MultilibBuilder> Ms;
   for (auto Element : RISCVMultilibSet) {
@@ -1766,7 +1891,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/Inputs/multilib_riscv_elf_sdk/lib/gcc/riscv64-unknown-elf/8.2.0/rv32ic/ilp32/crtbegin.o b/clang/test/Driver/Inputs/multilib_riscv_elf_sdk/lib/gcc/riscv64-unknown-elf/8.2.0/rv32ic/ilp32/crtbegin.o
new file mode 100644
index 000000000000000..e69de29bb2d1d64
diff --git a/clang/test/Driver/Inputs/multilib_riscv_elf_sdk/lib/gcc/riscv64-unknown-elf/8.2.0/rv32if/ilp32f/crtbegin.o b/clang/test/Driver/Inputs/multilib_riscv_elf_sdk/lib/gcc/riscv64-unknown-elf/8.2.0/rv32if/ilp32f/crtbegin.o
new file mode 100644
index 000000000000000..e69de29bb2d1d64
diff --git a/clang/test/Driver/Inputs/multilib_riscv_elf_sdk/lib/gcc/riscv64-unknown-elf/8.2.0/rv32ifc/ilp32/crtbegin.o b/clang/test/Driver/Inputs/multilib_riscv_elf_sdk/lib/gcc/riscv64-unknown-elf/8.2.0/rv32ifc/ilp32/crtbegin.o
new file mode 100644
index 000000000000000..e69de29bb2d1d64
diff --git a/clang/test/Driver/Inputs/multilib_riscv_elf_sdk/lib/gcc/riscv64-unknown-elf/8.2.0/rv32ifc/ilp32f/crtbegin.o b/clang/test/Driver/Inputs/multilib_riscv_elf_sdk/lib/gcc/riscv64-unknown-elf/8.2.0/rv32ifc/ilp32f/crtbegin.o
new file mode 100644
index 000000000000000..e69de29bb2d1d64
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 000000000000000..7a1bca76fee8ec4
--- /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=rv32ifc -mabi=ilp32f \
+// RUN:   | FileCheck -check-prefix=GCC-MULTI-LIB-REUSE-RV32IFC-ILP32F %s
+// GCC-MULTI-LIB-REUSE-RV32IFC-ILP32F: rv32if/ilp32f
+// GCC-MULTI-LIB-REUSE-RV32IFC-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=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: {{^.+$}}



More information about the cfe-commits mailing list