Skip to content

Commit

Permalink
[Driver] BareMetal ToolChain multilib layering
Browse files Browse the repository at this point in the history
This enables layering baremetal multilibs on top of each other.
For example a multilib containing only a no-exceptions libc++ could be
layered on top of a multilib containing C libs. This avoids the need
to duplicate the C library for every libc++ variant.

Differential Revision: https://reviews.llvm.org/D143075
  • Loading branch information
mplatings committed Jun 14, 2023
1 parent edc1130 commit ab2c80b
Show file tree
Hide file tree
Showing 3 changed files with 112 additions and 45 deletions.
107 changes: 62 additions & 45 deletions clang/lib/Driver/ToolChains/BareMetal.cpp
Expand Up @@ -103,9 +103,12 @@ BareMetal::BareMetal(const Driver &D, const llvm::Triple &Triple,
findMultilibs(D, Triple, Args);
SmallString<128> SysRoot(computeSysRoot());
if (!SysRoot.empty()) {
llvm::sys::path::append(SysRoot, "lib");
getFilePaths().push_back(std::string(SysRoot));
getLibraryPaths().push_back(std::string(SysRoot));
for (const Multilib &M : getOrderedMultilibs()) {
SmallString<128> Dir(SysRoot);
llvm::sys::path::append(Dir, M.osSuffix(), "lib");
getFilePaths().push_back(std::string(Dir));
getLibraryPaths().push_back(std::string(Dir));
}
}
}

Expand Down Expand Up @@ -222,10 +225,17 @@ Tool *BareMetal::buildLinker() const {
}

std::string BareMetal::computeSysRoot() const {
std::string Result = computeBaseSysRoot(getDriver(), getTriple());
return computeBaseSysRoot(getDriver(), getTriple());
}

BareMetal::OrderedMultilibs BareMetal::getOrderedMultilibs() const {
// Get multilibs in reverse order because they're ordered most-specific last.
if (!SelectedMultilibs.empty())
Result += SelectedMultilibs.back().osSuffix();
return Result;
return llvm::reverse(SelectedMultilibs);

// No multilibs selected so return a single default multilib.
static const llvm::SmallVector<Multilib> Default = {Multilib()};
return llvm::reverse(Default);
}

void BareMetal::AddClangSystemIncludeArgs(const ArgList &DriverArgs,
Expand All @@ -240,10 +250,14 @@ void BareMetal::AddClangSystemIncludeArgs(const ArgList &DriverArgs,
}

if (!DriverArgs.hasArg(options::OPT_nostdlibinc)) {
SmallString<128> Dir(computeSysRoot());
if (!Dir.empty()) {
llvm::sys::path::append(Dir, "include");
addSystemInclude(DriverArgs, CC1Args, Dir.str());
const SmallString<128> SysRoot(computeSysRoot());
if (!SysRoot.empty()) {
for (const Multilib &M : getOrderedMultilibs()) {
SmallString<128> Dir(SysRoot);
llvm::sys::path::append(Dir, M.includeSuffix());
llvm::sys::path::append(Dir, "include");
addSystemInclude(DriverArgs, CC1Args, Dir.str());
}
}
}
}
Expand All @@ -266,44 +280,47 @@ void BareMetal::AddClangCXXStdlibIncludeArgs(const ArgList &DriverArgs,
if (SysRoot.empty())
return;

switch (GetCXXStdlibType(DriverArgs)) {
case ToolChain::CST_Libcxx: {
// First check sysroot/usr/include/c++/v1 if it exists.
SmallString<128> TargetDir(SysRoot);
llvm::sys::path::append(TargetDir, "usr", "include", "c++", "v1");
if (D.getVFS().exists(TargetDir)) {
addSystemInclude(DriverArgs, CC1Args, TargetDir.str());
for (const Multilib &M : getOrderedMultilibs()) {
SmallString<128> Dir(SysRoot);
llvm::sys::path::append(Dir, M.gccSuffix());
switch (GetCXXStdlibType(DriverArgs)) {
case ToolChain::CST_Libcxx: {
// First check sysroot/usr/include/c++/v1 if it exists.
SmallString<128> TargetDir(Dir);
llvm::sys::path::append(TargetDir, "usr", "include", "c++", "v1");
if (D.getVFS().exists(TargetDir)) {
addSystemInclude(DriverArgs, CC1Args, TargetDir.str());
break;
}
// Add generic path if nothing else succeeded so far.
llvm::sys::path::append(Dir, "include", "c++", "v1");
addSystemInclude(DriverArgs, CC1Args, Dir.str());
break;
}
case ToolChain::CST_Libstdcxx: {
llvm::sys::path::append(Dir, "include", "c++");
std::error_code EC;
Generic_GCC::GCCVersion Version = {"", -1, -1, -1, "", "", ""};
// Walk the subdirs, and find the one with the newest gcc version:
for (llvm::vfs::directory_iterator
LI = D.getVFS().dir_begin(Dir.str(), EC),
LE;
!EC && LI != LE; LI = LI.increment(EC)) {
StringRef VersionText = llvm::sys::path::filename(LI->path());
auto CandidateVersion = Generic_GCC::GCCVersion::Parse(VersionText);
if (CandidateVersion.Major == -1)
continue;
if (CandidateVersion <= Version)
continue;
Version = CandidateVersion;
}
if (Version.Major != -1) {
llvm::sys::path::append(Dir, Version.Text);
addSystemInclude(DriverArgs, CC1Args, Dir.str());
}
break;
}
// Add generic path if nothing else succeeded so far.
SmallString<128> Dir(SysRoot);
llvm::sys::path::append(Dir, "include", "c++", "v1");
addSystemInclude(DriverArgs, CC1Args, Dir.str());
break;
}
case ToolChain::CST_Libstdcxx: {
SmallString<128> Dir(SysRoot);
llvm::sys::path::append(Dir, "include", "c++");
std::error_code EC;
Generic_GCC::GCCVersion Version = {"", -1, -1, -1, "", "", ""};
// Walk the subdirs, and find the one with the newest gcc version:
for (llvm::vfs::directory_iterator LI = D.getVFS().dir_begin(Dir.str(), EC),
LE;
!EC && LI != LE; LI = LI.increment(EC)) {
StringRef VersionText = llvm::sys::path::filename(LI->path());
auto CandidateVersion = Generic_GCC::GCCVersion::Parse(VersionText);
if (CandidateVersion.Major == -1)
continue;
if (CandidateVersion <= Version)
continue;
Version = CandidateVersion;
}
if (Version.Major == -1)
return;
llvm::sys::path::append(Dir, Version.Text);
addSystemInclude(DriverArgs, CC1Args, Dir.str());
break;
}
}
}

Expand Down
5 changes: 5 additions & 0 deletions clang/lib/Driver/ToolChains/BareMetal.h
Expand Up @@ -71,6 +71,11 @@ class LLVM_LIBRARY_VISIBILITY BareMetal : public ToolChain {
void AddLinkRuntimeLib(const llvm::opt::ArgList &Args,
llvm::opt::ArgStringList &CmdArgs) const;
std::string computeSysRoot() const override;

private:
using OrderedMultilibs =
llvm::iterator_range<llvm::SmallVector<Multilib>::const_reverse_iterator>;
OrderedMultilibs getOrderedMultilibs() const;
};

} // namespace toolchains
Expand Down
45 changes: 45 additions & 0 deletions clang/test/Driver/baremetal-multilib-layered.yaml
@@ -0,0 +1,45 @@
# REQUIRES: shell
# UNSUPPORTED: system-windows

# This test demonstrates "layered" multilib in which more than one
# multilib is matched.
# For example a multilib containing only a no-exceptions libc++ could
# be layered on top of a multilib containing C libs. This avoids the
# need to duplicate the C library for every libc++ variant.
# However -fno-exceptions is not yet supported for multilib selection
# so we use a more contrived -mfloat-abi example instead.

# RUN: rm -rf %T/baremetal_multilib_layered
# RUN: mkdir -p %T/baremetal_multilib_layered/bin
# RUN: mkdir -p %T/baremetal_multilib_layered/lib/clang-runtimes
# RUN: ln -s %clang %T/baremetal_multilib_layered/bin/clang
# RUN: ln -s %s %T/baremetal_multilib_layered/lib/clang-runtimes/multilib.yaml

# RUN: %T/baremetal_multilib_layered/bin/clang -no-canonical-prefixes -x c++ %s -### -o %t.out 2>&1 \
# RUN: --target=thumbv7m-none-eabi -mfloat-abi=softfp --sysroot= \
# RUN: | FileCheck -DSYSROOT=%T/baremetal_multilib_layered %s
# CHECK: "-cc1" "-triple" "thumbv7m-none-unknown-eabi"
# CHECK-SAME: "-internal-isystem" "[[SYSROOT]]/bin/../lib/clang-runtimes/softfp/include/c++/v1"
# CHECK-SAME: "-internal-isystem" "[[SYSROOT]]/bin/../lib/clang-runtimes/soft/include/c++/v1"
# CHECK-SAME: "-internal-isystem" "[[SYSROOT]]/bin/../lib/clang-runtimes/softfp/include"
# CHECK-SAME: "-internal-isystem" "[[SYSROOT]]/bin/../lib/clang-runtimes/soft/include"
# CHECK-NEXT: "-L[[SYSROOT]]/bin/../lib/clang-runtimes/softfp/lib"
# CHECK-SAME: "-L[[SYSROOT]]/bin/../lib/clang-runtimes/soft/lib"

# RUN: %T/baremetal_multilib_layered/bin/clang -no-canonical-prefixes -print-multi-directory 2>&1 \
# RUN: --target=arm-none-eabi -mfloat-abi=softfp --sysroot= \
# RUN: | FileCheck --check-prefix=CHECK-PRINT-MULTI-DIRECTORY %s
# CHECK-PRINT-MULTI-DIRECTORY: soft
# CHECK-PRINT-MULTI-DIRECTORY-NEXT: softfp

---
MultilibVersion: 1.0
Variants:
- Dir: soft
Flags: [-mfloat-abi=soft]
- Dir: softfp
Flags: [-mfloat-abi=softfp]
Mappings:
- Match: -mfloat-abi=softfp
Flags: [-mfloat-abi=soft]
...

0 comments on commit ab2c80b

Please sign in to comment.