Skip to content

Commit 20b2dca

Browse files
committed
Reland [Driver] Add support for GCC installation detection in Baremetal toolchain
This patch introduces enhancements to the Baremetal toolchain to support GCC toolchain detection. - If the --gcc-install-dir or --gcc-toolchain options are provided and point to valid paths, the sysroot is derived from those locations. - If not, the logic falls back to the existing sysroot inference mechanism already present in the Baremetal toolchain. - Support for adding include paths for the libstdc++ library has also been added. Additionally, the restriction to always use the integrated assembler has been removed. With a valid GCC installation, the GNU assembler can now be used as well. This patch currently updates and adds tests for the ARM target only. RISC-V-specific tests will be introduced in a later patch, once the RISCVToolChain is fully merged into the Baremetal toolchain. At this stage, there is no way to test the RISC-V target within this PR. RFC: https: //discourse.llvm.org/t/merging-riscvtoolchain-and-baremetal-toolchains/75524 This PR include fixes to tests which were failing on fuchsia builders Change-Id: I5cd1d949297fee025b62ea6037c554bd4a0eeda1
1 parent ad9e591 commit 20b2dca

File tree

32 files changed

+418
-64
lines changed

32 files changed

+418
-64
lines changed

clang/docs/Toolchain.rst

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -347,3 +347,8 @@ workarounds for issues discovered in libstdc++, and these are removed
347347
as fixed libstdc++ becomes sufficiently old.
348348

349349
You can instruct Clang to use libstdc++ with the ``-stdlib=libstdc++`` flag.
350+
351+
GCC Installation
352+
=================
353+
Users can point to their GCC installation by using the ``-gcc-toolchain`` or by
354+
using ``-gcc-install-dir`` flag.

clang/include/clang/Basic/DiagnosticDriverKinds.td

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -847,6 +847,9 @@ def note_drv_available_multilibs : Note<
847847
"available multilibs are:%0">;
848848
def err_drv_multilib_custom_error : Error<
849849
"multilib configuration error: %0">;
850+
def warn_drv_multilib_not_available_for_target: Warning<
851+
"no multilib structure encoded for Arm, Aarch64 and PPC targets">,
852+
InGroup<DiagGroup<"multilib-not-found">>;
850853

851854
def err_drv_experimental_crel : Error<
852855
"-Wa,--allow-experimental-crel must be specified to use -Wa,--crel. "

clang/lib/Driver/ToolChains/BareMetal.cpp

Lines changed: 174 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,40 @@ using namespace clang::driver;
3131
using namespace clang::driver::tools;
3232
using namespace clang::driver::toolchains;
3333

34+
/// Is the triple {aarch64.aarch64_be}-none-elf?
35+
static bool isAArch64BareMetal(const llvm::Triple &Triple) {
36+
if (Triple.getArch() != llvm::Triple::aarch64 &&
37+
Triple.getArch() != llvm::Triple::aarch64_be)
38+
return false;
39+
40+
if (Triple.getVendor() != llvm::Triple::UnknownVendor)
41+
return false;
42+
43+
if (Triple.getOS() != llvm::Triple::UnknownOS)
44+
return false;
45+
46+
return Triple.getEnvironmentName() == "elf";
47+
}
48+
49+
static bool isRISCVBareMetal(const llvm::Triple &Triple) {
50+
if (!Triple.isRISCV())
51+
return false;
52+
53+
if (Triple.getVendor() != llvm::Triple::UnknownVendor)
54+
return false;
55+
56+
if (Triple.getOS() != llvm::Triple::UnknownOS)
57+
return false;
58+
59+
return Triple.getEnvironmentName() == "elf";
60+
}
61+
62+
/// Is the triple powerpc[64][le]-*-none-eabi?
63+
static bool isPPCBareMetal(const llvm::Triple &Triple) {
64+
return Triple.isPPC() && Triple.getOS() == llvm::Triple::UnknownOS &&
65+
Triple.getEnvironment() == llvm::Triple::EABI;
66+
}
67+
3468
static bool findRISCVMultilibs(const Driver &D,
3569
const llvm::Triple &TargetTriple,
3670
const ArgList &Args, DetectedMultilibs &Result) {
@@ -95,7 +129,8 @@ static bool findRISCVMultilibs(const Driver &D,
95129
return false;
96130
}
97131

98-
static std::string computeBaseSysRoot(const Driver &D, bool IncludeTriple) {
132+
static std::string computeClangRuntimesSysRoot(const Driver &D,
133+
bool IncludeTriple) {
99134
if (!D.SysRoot.empty())
100135
return D.SysRoot;
101136

@@ -108,56 +143,123 @@ static std::string computeBaseSysRoot(const Driver &D, bool IncludeTriple) {
108143
return std::string(SysRootDir);
109144
}
110145

111-
BareMetal::BareMetal(const Driver &D, const llvm::Triple &Triple,
112-
const ArgList &Args)
113-
: ToolChain(D, Triple, Args),
114-
SysRoot(computeBaseSysRoot(D, /*IncludeTriple=*/true)) {
115-
getProgramPaths().push_back(getDriver().Dir);
116-
117-
findMultilibs(D, Triple, Args);
118-
SmallString<128> SysRoot(computeSysRoot());
119-
if (!SysRoot.empty()) {
120-
for (const Multilib &M : getOrderedMultilibs()) {
121-
SmallString<128> Dir(SysRoot);
122-
llvm::sys::path::append(Dir, M.osSuffix(), "lib");
123-
getFilePaths().push_back(std::string(Dir));
124-
getLibraryPaths().push_back(std::string(Dir));
125-
}
146+
// Only consider the GCC toolchain based on the values provided through the
147+
// `--gcc-toolchain` and `--gcc-install-dir` flags. The function below returns
148+
// whether the GCC toolchain was initialized successfully.
149+
bool BareMetal::initGCCInstallation(const llvm::Triple &Triple,
150+
const llvm::opt::ArgList &Args) {
151+
if (Args.getLastArg(options::OPT_gcc_toolchain) ||
152+
Args.getLastArg(clang::driver::options::OPT_gcc_install_dir_EQ)) {
153+
GCCInstallation.init(Triple, Args);
154+
return GCCInstallation.isValid();
126155
}
156+
return false;
127157
}
128158

129-
/// Is the triple {aarch64.aarch64_be}-none-elf?
130-
static bool isAArch64BareMetal(const llvm::Triple &Triple) {
131-
if (Triple.getArch() != llvm::Triple::aarch64 &&
132-
Triple.getArch() != llvm::Triple::aarch64_be)
133-
return false;
134-
135-
if (Triple.getVendor() != llvm::Triple::UnknownVendor)
136-
return false;
137-
138-
if (Triple.getOS() != llvm::Triple::UnknownOS)
139-
return false;
140-
141-
return Triple.getEnvironmentName() == "elf";
159+
// This logic is adapted from RISCVToolChain.cpp as part of the ongoing effort
160+
// to merge RISCVToolChain into the Baremetal toolchain. It infers the presence
161+
// of a valid GCC toolchain by checking whether the `crt0.o` file exists in the
162+
// `bin/../<target-triple>/lib` directory.
163+
static bool detectGCCToolchainAdjacent(const Driver &D) {
164+
SmallString<128> GCCDir;
165+
llvm::sys::path::append(GCCDir, D.Dir, "..", D.getTargetTriple(),
166+
"lib/crt0.o");
167+
return llvm::sys::fs::exists(GCCDir);
142168
}
143169

144-
static bool isRISCVBareMetal(const llvm::Triple &Triple) {
145-
if (!Triple.isRISCV())
146-
return false;
170+
// If no sysroot is provided the driver will first attempt to infer it from the
171+
// values of `--gcc-install-dir` or `--gcc-toolchain`, which specify the
172+
// location of a GCC toolchain.
173+
// If neither flag is used, the sysroot defaults to either:
174+
//    - `bin/../<target-triple>`
175+
//    - `bin/../lib/clang-runtimes/<target-triple>`
176+
//
177+
// To use the `clang-runtimes` path, ensure that `../<target-triple>/lib/crt0.o`
178+
// does not exist relative to the driver.
179+
std::string BareMetal::computeSysRoot() const {
180+
// Use Baremetal::sysroot if it has already been set.
181+
if (!SysRoot.empty())
182+
return SysRoot;
183+
184+
// Use the sysroot specified via the `--sysroot` command-line flag, if
185+
// provided.
186+
const Driver &D = getDriver();
187+
if (!D.SysRoot.empty())
188+
return D.SysRoot;
147189

148-
if (Triple.getVendor() != llvm::Triple::UnknownVendor)
149-
return false;
190+
// Attempt to infer sysroot from a valid GCC installation.
191+
// If no valid GCC installation, check for a GCC toolchain alongside Clang.
192+
SmallString<128> inferredSysRoot;
193+
if (IsGCCInstallationValid) {
194+
llvm::sys::path::append(inferredSysRoot, GCCInstallation.getParentLibPath(),
195+
"..", GCCInstallation.getTriple().str());
196+
} else if (detectGCCToolchainAdjacent(D)) {
197+
// Use the triple as provided to the driver. Unlike the parsed triple
198+
// this has not been normalized to always contain every field.
199+
llvm::sys::path::append(inferredSysRoot, D.Dir, "..", D.getTargetTriple());
200+
}
201+
// If a valid sysroot was inferred and exists, use it
202+
if (!inferredSysRoot.empty() && llvm::sys::fs::exists(inferredSysRoot))
203+
return std::string(inferredSysRoot);
150204

151-
if (Triple.getOS() != llvm::Triple::UnknownOS)
152-
return false;
205+
// Use the clang-runtimes path.
206+
return computeClangRuntimesSysRoot(D, /*IncludeTriple*/ true);
207+
}
153208

154-
return Triple.getEnvironmentName() == "elf";
209+
static void addMultilibsFilePaths(const Driver &D, const MultilibSet &Multilibs,
210+
const Multilib &Multilib,
211+
StringRef InstallPath,
212+
ToolChain::path_list &Paths) {
213+
if (const auto &PathsCallback = Multilibs.filePathsCallback())
214+
for (const auto &Path : PathsCallback(Multilib))
215+
addPathIfExists(D, InstallPath + Path, Paths);
155216
}
156217

157-
/// Is the triple powerpc[64][le]-*-none-eabi?
158-
static bool isPPCBareMetal(const llvm::Triple &Triple) {
159-
return Triple.isPPC() && Triple.getOS() == llvm::Triple::UnknownOS &&
160-
Triple.getEnvironment() == llvm::Triple::EABI;
218+
// GCC mutltilibs will only work for those targets that have their multlib
219+
// structure encoded into GCCInstallation. Baremetal toolchain supports ARM,
220+
// AArch64, RISCV and PPC and of these only RISCV have GCC multilibs hardcoded
221+
// in GCCInstallation.
222+
BareMetal::BareMetal(const Driver &D, const llvm::Triple &Triple,
223+
const ArgList &Args)
224+
: Generic_ELF(D, Triple, Args) {
225+
IsGCCInstallationValid = initGCCInstallation(Triple, Args);
226+
std::string ComputedSysRoot = computeSysRoot();
227+
if (IsGCCInstallationValid) {
228+
if (!isRISCVBareMetal(Triple))
229+
D.Diag(clang::diag::warn_drv_multilib_not_available_for_target);
230+
231+
Multilibs = GCCInstallation.getMultilibs();
232+
SelectedMultilibs.assign({GCCInstallation.getMultilib()});
233+
234+
path_list &Paths = getFilePaths();
235+
// Add toolchain/multilib specific file paths.
236+
addMultilibsFilePaths(D, Multilibs, SelectedMultilibs.back(),
237+
GCCInstallation.getInstallPath(), Paths);
238+
// Adding filepath for locating crt{begin,end}.o files.
239+
Paths.push_back(GCCInstallation.getInstallPath().str());
240+
// Adding filepath for locating crt0.o file.
241+
Paths.push_back(ComputedSysRoot + "/lib");
242+
243+
ToolChain::path_list &PPaths = getProgramPaths();
244+
// Multilib cross-compiler GCC installations put ld in a triple-prefixed
245+
// directory off of the parent of the GCC installation.
246+
PPaths.push_back(Twine(GCCInstallation.getParentLibPath() + "/../" +
247+
GCCInstallation.getTriple().str() + "/bin")
248+
.str());
249+
PPaths.push_back((GCCInstallation.getParentLibPath() + "/../bin").str());
250+
} else {
251+
getProgramPaths().push_back(getDriver().Dir);
252+
findMultilibs(D, Triple, Args);
253+
const SmallString<128> SysRootDir(computeSysRoot());
254+
if (!SysRootDir.empty()) {
255+
for (const Multilib &M : getOrderedMultilibs()) {
256+
SmallString<128> Dir(SysRootDir);
257+
llvm::sys::path::append(Dir, M.osSuffix(), "lib");
258+
getFilePaths().push_back(std::string(Dir));
259+
getLibraryPaths().push_back(std::string(Dir));
260+
}
261+
}
262+
}
161263
}
162264

163265
static void
@@ -216,7 +318,7 @@ getMultilibConfigPath(const Driver &D, const llvm::Triple &Triple,
216318
return {};
217319
}
218320
} else {
219-
MultilibPath = computeBaseSysRoot(D, /*IncludeTriple=*/false);
321+
MultilibPath = computeClangRuntimesSysRoot(D, /*IncludeTriple=*/false);
220322
llvm::sys::path::append(MultilibPath, MultilibFilename);
221323
}
222324
return MultilibPath;
@@ -234,15 +336,15 @@ void BareMetal::findMultilibs(const Driver &D, const llvm::Triple &Triple,
234336
if (D.getVFS().exists(*MultilibPath)) {
235337
// If multilib.yaml is found, update sysroot so it doesn't use a target
236338
// specific suffix
237-
SysRoot = computeBaseSysRoot(D, /*IncludeTriple=*/false);
339+
SysRoot = computeClangRuntimesSysRoot(D, /*IncludeTriple=*/false);
238340
SmallVector<StringRef> CustomFlagMacroDefines;
239341
findMultilibsFromYAML(*this, D, *MultilibPath, Args, Result,
240342
CustomFlagMacroDefines);
241343
SelectedMultilibs = Result.SelectedMultilibs;
242344
Multilibs = Result.Multilibs;
243345
MultilibMacroDefines.append(CustomFlagMacroDefines.begin(),
244346
CustomFlagMacroDefines.end());
245-
} else if (isRISCVBareMetal(Triple)) {
347+
} else if (isRISCVBareMetal(Triple) && !detectGCCToolchainAdjacent(D)) {
246348
if (findRISCVMultilibs(D, Triple, Args, Result)) {
247349
SelectedMultilibs = Result.SelectedMultilibs;
248350
Multilibs = Result.Multilibs;
@@ -263,8 +365,6 @@ Tool *BareMetal::buildStaticLibTool() const {
263365
return new tools::baremetal::StaticLibTool(*this);
264366
}
265367

266-
std::string BareMetal::computeSysRoot() const { return SysRoot; }
267-
268368
BareMetal::OrderedMultilibs BareMetal::getOrderedMultilibs() const {
269369
// Get multilibs in reverse order because they're ordered most-specific last.
270370
if (!SelectedMultilibs.empty())
@@ -292,10 +392,10 @@ void BareMetal::AddClangSystemIncludeArgs(const ArgList &DriverArgs,
292392
if (std::optional<std::string> Path = getStdlibIncludePath())
293393
addSystemInclude(DriverArgs, CC1Args, *Path);
294394

295-
const SmallString<128> SysRoot(computeSysRoot());
296-
if (!SysRoot.empty()) {
395+
const SmallString<128> SysRootDir(computeSysRoot());
396+
if (!SysRootDir.empty()) {
297397
for (const Multilib &M : getOrderedMultilibs()) {
298-
SmallString<128> Dir(SysRoot);
398+
SmallString<128> Dir(SysRootDir);
299399
llvm::sys::path::append(Dir, M.includeSuffix());
300400
llvm::sys::path::append(Dir, "include");
301401
addSystemInclude(DriverArgs, CC1Args, Dir.str());
@@ -309,6 +409,19 @@ void BareMetal::addClangTargetOptions(const ArgList &DriverArgs,
309409
CC1Args.push_back("-nostdsysteminc");
310410
}
311411

412+
void BareMetal::addLibStdCxxIncludePaths(
413+
const llvm::opt::ArgList &DriverArgs,
414+
llvm::opt::ArgStringList &CC1Args) const {
415+
if (!IsGCCInstallationValid)
416+
return;
417+
const GCCVersion &Version = GCCInstallation.getVersion();
418+
StringRef TripleStr = GCCInstallation.getTriple().str();
419+
const Multilib &Multilib = GCCInstallation.getMultilib();
420+
addLibStdCXXIncludePaths(computeSysRoot() + "/include/c++/" + Version.Text,
421+
TripleStr, Multilib.includeSuffix(), DriverArgs,
422+
CC1Args);
423+
}
424+
312425
void BareMetal::AddClangCXXStdlibIncludeArgs(const ArgList &DriverArgs,
313426
ArgStringList &CC1Args) const {
314427
if (DriverArgs.hasArg(options::OPT_nostdinc, options::OPT_nostdlibinc,
@@ -339,23 +452,23 @@ void BareMetal::AddClangCXXStdlibIncludeArgs(const ArgList &DriverArgs,
339452
};
340453

341454
switch (GetCXXStdlibType(DriverArgs)) {
342-
case ToolChain::CST_Libcxx: {
343-
SmallString<128> P(D.Dir);
344-
llvm::sys::path::append(P, "..", "include");
345-
AddCXXIncludePath(P);
346-
break;
347-
}
348-
case ToolChain::CST_Libstdcxx:
349-
// We only support libc++ toolchain installation.
350-
break;
455+
case ToolChain::CST_Libcxx: {
456+
SmallString<128> P(D.Dir);
457+
llvm::sys::path::append(P, "..", "include");
458+
AddCXXIncludePath(P);
459+
break;
460+
}
461+
case ToolChain::CST_Libstdcxx:
462+
addLibStdCxxIncludePaths(DriverArgs, CC1Args);
463+
break;
351464
}
352465

353-
std::string SysRoot(computeSysRoot());
354-
if (SysRoot.empty())
466+
std::string SysRootDir(computeSysRoot());
467+
if (SysRootDir.empty())
355468
return;
356469

357470
for (const Multilib &M : getOrderedMultilibs()) {
358-
SmallString<128> Dir(SysRoot);
471+
SmallString<128> Dir(SysRootDir);
359472
llvm::sys::path::append(Dir, M.gccSuffix());
360473
switch (GetCXXStdlibType(DriverArgs)) {
361474
case ToolChain::CST_Libcxx: {

0 commit comments

Comments
 (0)