Skip to content

Commit

Permalink
[RISCV] Implement multi-lib reuse rule for RISC-V bare-metal toolchain (
Browse files Browse the repository at this point in the history
#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.
  • Loading branch information
4vtomat committed Dec 18, 2023
1 parent b83b287 commit 111a229
Show file tree
Hide file tree
Showing 2 changed files with 207 additions and 1 deletion.
127 changes: 126 additions & 1 deletion clang/lib/Driver/ToolChains/Gnu.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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>
Expand Down Expand Up @@ -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 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.
static bool
selectRISCVMultilib(const MultilibSet &RISCVMultilibSet, StringRef Arch,
const Multilib::flags_list &Flags,
llvm::SmallVectorImpl<Multilib> &SelectedMultilibs) {
// Try to find the perfect matching multi-lib first.
if (RISCVMultilibSet.select(Flags, SelectedMultilibs))
return true;

Multilib::flags_list NewFlags;
std::vector<MultilibBuilder> NewMultilibs;

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 be handled in another place.
consumeError(ParseResult.takeError());
return false;
}

auto &ISAInfo = *ParseResult;

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 separated
// extension. e.g. march=rv32im -> +i +m
for (const auto &M : RISCVMultilibSet) {
bool Skip = false;

MultilibBuilder NewMultilib =
MultilibBuilder(M.gccSuffix(), M.osSuffix(), M.includeSuffix());
for (StringRef Flag : M.flags()) {
// Add back all flags except -march.
if (!Flag.consume_front("-march=")) {
NewMultilib.flag(Flag);
continue;
}

// Break down -march into individual extension.
llvm::Expected<std::unique_ptr<llvm::RISCVISAInfo>> MLConfigParseResult =
llvm::RISCVISAInfo::parseArchString(
Flag, /*EnableExperimentalExtension=*/true,
/*ExperimentalExtensionVersionCheck=*/false);
if (!MLConfigParseResult) {
// Ignore any error here, we assume it will handled in another place.
llvm::consumeError(MLConfigParseResult.takeError());

// We might get a 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;

const llvm::RISCVISAInfo::OrderedExtensionMap &MLConfigArchExts =
MLConfigISAInfo->getExtensions();
for (auto MLConfigArchExt : MLConfigArchExts) {
auto ExtName = MLConfigArchExt.first;
NewMultilib.flag(Twine("-", ExtName).str());

if (AllArchExts.insert(ExtName).second) {
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 (const 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,
Expand Down Expand Up @@ -1766,7 +1890,8 @@ static void findRISCVBareMetalMultilibs(const Driver &D,
}
}

if (RISCVMultilibs.select(Flags, Result.SelectedMultilibs))
if (selectRISCVMultilib(RISCVMultilibs, MArch, Flags,
Result.SelectedMultilibs))
Result.Multilibs = RISCVMultilibs;
}

Expand Down
81 changes: 81 additions & 0 deletions clang/test/Driver/riscv-toolchain-gcc-multilib-reuse.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
// 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: {{^.+$}}

1 comment on commit 111a229

@cor3ntin
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This seems to break CI builds on github, could you take a look? Thanks
https://buildkite.com/llvm-project/github-pull-requests/builds/23557#018c7c6f-a99b-4a70-ad54-c4da1cc33197

Please sign in to comment.