[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