Frederik Harwath d6fae7f921
Reapply "[Clang] Take libstdc++ into account during GCC detection" #145056 (#154487)
The Generic_GCC::GCCInstallationDetector class picks the GCC installation directory with the largest version number. Since the location of the libstdc++ include directories is tied to the GCC version, this can break C++ compilation if the libstdc++ headers for this particular GCC version are not available. Linux distributions tend to package the libstdc++ headers separately from GCC. This frequently leads to situations in which a newer version of GCC gets installed as a dependency of another package without installing the corresponding libstdc++ package. Clang then fails to compile C++ code because it cannot find the libstdc++ headers. Since libstdc++ headers are in fact installed on the system, the GCC installation continues to work, the user may not be aware of the details of the GCC detection, and the compiler does not recognize the situation and emit a warning, this behavior can be hard to understand - as witnessed by many related bug reports over the years.

The goal of this work is to change the GCC detection to prefer GCC installations that contain libstdc++ include directories over those which do not. This should happen regardless of the input language since picking different GCC installations for a build that mixes C and C++ might lead to incompatibilities.
Any change to the GCC installation detection will probably have a negative impact on some users. For instance, for a C user who relies on using the GCC installation with the largest version number, it might become necessary to use the --gcc-install-dir option to ensure that this GCC version is selected.
This seems like an acceptable trade-off given that the situation for users who do not have any special demands on the particular GCC installation directory would be improved significantly.
 
This patch does not yet change the automatic GCC installation directory choice. Instead, it does introduce a warning that informs the user about the future change if the chosen GCC installation directory differs from the one that would be chosen if the libstdc++ headers are taken into account.

See also this related Discourse discussion: https://discourse.llvm.org/t/rfc-take-libstdc-into-account-during-gcc-detection/86992.

This patch reapplies #145056. The test in the original PR did not specify a target in the clang RUN line and used a wrong way of piping to FileCheck.
2025-08-22 07:39:11 +02:00

3461 lines
137 KiB
C++

//===--- Gnu.cpp - Gnu Tool and ToolChain Implementations -------*- C++ -*-===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
#include "Gnu.h"
#include "Arch/ARM.h"
#include "Arch/CSKY.h"
#include "Arch/LoongArch.h"
#include "Arch/Mips.h"
#include "Arch/PPC.h"
#include "Arch/RISCV.h"
#include "Arch/Sparc.h"
#include "Arch/SystemZ.h"
#include "clang/Config/config.h" // for GCC_INSTALL_PREFIX
#include "clang/Driver/CommonArgs.h"
#include "clang/Driver/Compilation.h"
#include "clang/Driver/Driver.h"
#include "clang/Driver/MultilibBuilder.h"
#include "clang/Driver/Options.h"
#include "clang/Driver/Tool.h"
#include "clang/Driver/ToolChain.h"
#include "llvm/ADT/StringSet.h"
#include "llvm/ADT/Twine.h"
#include "llvm/Option/ArgList.h"
#include "llvm/Support/CodeGen.h"
#include "llvm/Support/Path.h"
#include "llvm/Support/VirtualFileSystem.h"
#include "llvm/TargetParser/RISCVISAInfo.h"
#include "llvm/TargetParser/TargetParser.h"
#include <system_error>
using namespace clang::driver;
using namespace clang::driver::toolchains;
using namespace clang;
using namespace llvm::opt;
using tools::addMultilibFlag;
using tools::addPathIfExists;
static bool forwardToGCC(const Option &O) {
// LinkerInput options have been forwarded. Don't duplicate.
if (O.hasFlag(options::LinkerInput))
return false;
return O.matches(options::OPT_Link_Group) || O.hasFlag(options::LinkOption);
}
// Switch CPU names not recognized by GNU assembler to a close CPU that it does
// recognize, instead of a lower march from being picked in the absence of a cpu
// flag.
static void normalizeCPUNamesForAssembler(const ArgList &Args,
ArgStringList &CmdArgs) {
if (Arg *A = Args.getLastArg(options::OPT_mcpu_EQ)) {
StringRef CPUArg(A->getValue());
if (CPUArg.equals_insensitive("krait"))
CmdArgs.push_back("-mcpu=cortex-a15");
else if (CPUArg.equals_insensitive("kryo"))
CmdArgs.push_back("-mcpu=cortex-a57");
else
Args.AddLastArg(CmdArgs, options::OPT_mcpu_EQ);
}
}
void tools::gcc::Common::ConstructJob(Compilation &C, const JobAction &JA,
const InputInfo &Output,
const InputInfoList &Inputs,
const ArgList &Args,
const char *LinkingOutput) const {
const Driver &D = getToolChain().getDriver();
ArgStringList CmdArgs;
for (const auto &A : Args) {
if (forwardToGCC(A->getOption())) {
// It is unfortunate that we have to claim here, as this means
// we will basically never report anything interesting for
// platforms using a generic gcc, even if we are just using gcc
// to get to the assembler.
A->claim();
A->render(Args, CmdArgs);
}
}
RenderExtraToolArgs(JA, CmdArgs);
// If using a driver, force the arch.
if (getToolChain().getTriple().isOSDarwin()) {
CmdArgs.push_back("-arch");
CmdArgs.push_back(
Args.MakeArgString(getToolChain().getDefaultUniversalArchName()));
}
// Try to force gcc to match the tool chain we want, if we recognize
// the arch.
//
// FIXME: The triple class should directly provide the information we want
// here.
switch (getToolChain().getArch()) {
default:
break;
case llvm::Triple::x86:
case llvm::Triple::ppc:
case llvm::Triple::ppcle:
CmdArgs.push_back("-m32");
break;
case llvm::Triple::x86_64:
case llvm::Triple::ppc64:
case llvm::Triple::ppc64le:
CmdArgs.push_back("-m64");
break;
case llvm::Triple::sparcel:
CmdArgs.push_back("-EL");
break;
}
assert((Output.isFilename() || Output.isNothing()) && "Invalid output.");
if (Output.isFilename()) {
CmdArgs.push_back("-o");
CmdArgs.push_back(Output.getFilename());
} else {
CmdArgs.push_back("-fsyntax-only");
}
Args.AddAllArgValues(CmdArgs, options::OPT_Wa_COMMA, options::OPT_Xassembler);
// Only pass -x if gcc will understand it; otherwise hope gcc
// understands the suffix correctly. The main use case this would go
// wrong in is for linker inputs if they happened to have an odd
// suffix; really the only way to get this to happen is a command
// like '-x foobar a.c' which will treat a.c like a linker input.
//
// FIXME: For the linker case specifically, can we safely convert
// inputs into '-Wl,' options?
for (const auto &II : Inputs) {
// Don't try to pass LLVM or AST inputs to a generic gcc.
if (types::isLLVMIR(II.getType()))
D.Diag(clang::diag::err_drv_no_linker_llvm_support)
<< getToolChain().getTripleString();
else if (II.getType() == types::TY_AST)
D.Diag(diag::err_drv_no_ast_support) << getToolChain().getTripleString();
else if (II.getType() == types::TY_ModuleFile)
D.Diag(diag::err_drv_no_module_support)
<< getToolChain().getTripleString();
if (types::canTypeBeUserSpecified(II.getType())) {
CmdArgs.push_back("-x");
CmdArgs.push_back(types::getTypeName(II.getType()));
}
if (II.isFilename())
CmdArgs.push_back(II.getFilename());
else {
const Arg &A = II.getInputArg();
// Reverse translate some rewritten options.
if (A.getOption().matches(options::OPT_Z_reserved_lib_stdcxx)) {
CmdArgs.push_back("-lstdc++");
continue;
}
// Don't render as input, we need gcc to do the translations.
A.render(Args, CmdArgs);
}
}
const std::string &customGCCName = D.getCCCGenericGCCName();
const char *GCCName;
if (!customGCCName.empty())
GCCName = customGCCName.c_str();
else if (D.CCCIsCXX()) {
GCCName = "g++";
} else
GCCName = "gcc";
const char *Exec = Args.MakeArgString(getToolChain().GetProgramPath(GCCName));
C.addCommand(std::make_unique<Command>(JA, *this,
ResponseFileSupport::AtFileCurCP(),
Exec, CmdArgs, Inputs, Output));
}
void tools::gcc::Preprocessor::RenderExtraToolArgs(
const JobAction &JA, ArgStringList &CmdArgs) const {
CmdArgs.push_back("-E");
}
void tools::gcc::Compiler::RenderExtraToolArgs(const JobAction &JA,
ArgStringList &CmdArgs) const {
const Driver &D = getToolChain().getDriver();
switch (JA.getType()) {
// If -flto, etc. are present then make sure not to force assembly output.
case types::TY_LLVM_IR:
case types::TY_LTO_IR:
case types::TY_LLVM_BC:
case types::TY_LTO_BC:
CmdArgs.push_back("-c");
break;
// We assume we've got an "integrated" assembler in that gcc will produce an
// object file itself.
case types::TY_Object:
CmdArgs.push_back("-c");
break;
case types::TY_PP_Asm:
CmdArgs.push_back("-S");
break;
case types::TY_Nothing:
CmdArgs.push_back("-fsyntax-only");
break;
default:
D.Diag(diag::err_drv_invalid_gcc_output_type) << getTypeName(JA.getType());
}
}
void tools::gcc::Linker::RenderExtraToolArgs(const JobAction &JA,
ArgStringList &CmdArgs) const {
// The types are (hopefully) good enough.
}
static bool getStatic(const ArgList &Args) {
return Args.hasArg(options::OPT_static) &&
!Args.hasArg(options::OPT_static_pie);
}
void tools::gnutools::StaticLibTool::ConstructJob(
Compilation &C, const JobAction &JA, const InputInfo &Output,
const InputInfoList &Inputs, const ArgList &Args,
const char *LinkingOutput) const {
const Driver &D = getToolChain().getDriver();
// Silence warning for "clang -g foo.o -o foo"
Args.ClaimAllArgs(options::OPT_g_Group);
// and "clang -emit-llvm foo.o -o foo"
Args.ClaimAllArgs(options::OPT_emit_llvm);
// and for "clang -w foo.o -o foo". Other warning options are already
// handled somewhere else.
Args.ClaimAllArgs(options::OPT_w);
// Silence warnings when linking C code with a C++ '-stdlib' argument.
Args.ClaimAllArgs(options::OPT_stdlib_EQ);
// ar tool command "llvm-ar <options> <output_file> <input_files>".
ArgStringList CmdArgs;
// Create and insert file members with a deterministic index.
CmdArgs.push_back("rcsD");
CmdArgs.push_back(Output.getFilename());
for (const auto &II : Inputs) {
if (II.isFilename()) {
CmdArgs.push_back(II.getFilename());
}
}
// Delete old output archive file if it already exists before generating a new
// archive file.
auto OutputFileName = Output.getFilename();
if (Output.isFilename() && llvm::sys::fs::exists(OutputFileName)) {
if (std::error_code EC = llvm::sys::fs::remove(OutputFileName)) {
D.Diag(diag::err_drv_unable_to_remove_file) << EC.message();
return;
}
}
const char *Exec = Args.MakeArgString(getToolChain().GetStaticLibToolPath());
C.addCommand(std::make_unique<Command>(JA, *this,
ResponseFileSupport::AtFileCurCP(),
Exec, CmdArgs, Inputs, Output));
}
void tools::gnutools::Linker::ConstructJob(Compilation &C, const JobAction &JA,
const InputInfo &Output,
const InputInfoList &Inputs,
const ArgList &Args,
const char *LinkingOutput) const {
// FIXME: The Linker class constructor takes a ToolChain and not a
// Generic_ELF, so the static_cast might return a reference to a invalid
// instance (see PR45061). Ideally, the Linker constructor needs to take a
// Generic_ELF instead.
const auto &ToolChain = static_cast<const Generic_ELF &>(getToolChain());
const Driver &D = ToolChain.getDriver();
const llvm::Triple &Triple = getToolChain().getEffectiveTriple();
const llvm::Triple::ArchType Arch = ToolChain.getArch();
const bool isOHOSFamily = ToolChain.getTriple().isOHOSFamily();
const bool isAndroid = ToolChain.getTriple().isAndroid();
const bool IsIAMCU = ToolChain.getTriple().isOSIAMCU();
const bool IsVE = ToolChain.getTriple().isVE();
const bool IsStaticPIE = getStaticPIE(Args, ToolChain);
const bool IsStatic = getStatic(Args);
const bool HasCRTBeginEndFiles =
ToolChain.getTriple().hasEnvironment() ||
(ToolChain.getTriple().getVendor() != llvm::Triple::MipsTechnologies);
ArgStringList CmdArgs;
// Silence warning for "clang -g foo.o -o foo"
Args.ClaimAllArgs(options::OPT_g_Group);
// and "clang -emit-llvm foo.o -o foo"
Args.ClaimAllArgs(options::OPT_emit_llvm);
// and for "clang -w foo.o -o foo". Other warning options are already
// handled somewhere else.
Args.ClaimAllArgs(options::OPT_w);
if (!D.SysRoot.empty())
CmdArgs.push_back(Args.MakeArgString("--sysroot=" + D.SysRoot));
if (Args.hasArg(options::OPT_s))
CmdArgs.push_back("-s");
if (Triple.isARM() || Triple.isThumb()) {
bool IsBigEndian = arm::isARMBigEndian(Triple, Args);
if (IsBigEndian)
arm::appendBE8LinkFlag(Args, CmdArgs, Triple);
CmdArgs.push_back(IsBigEndian ? "-EB" : "-EL");
} else if (Triple.isAArch64()) {
CmdArgs.push_back(Arch == llvm::Triple::aarch64_be ? "-EB" : "-EL");
}
// Most Android ARM64 targets should enable the linker fix for erratum
// 843419. Only non-Cortex-A53 devices are allowed to skip this flag.
if (Arch == llvm::Triple::aarch64 && (isAndroid || isOHOSFamily) &&
Args.hasFlag(options::OPT_mfix_cortex_a53_843419,
options::OPT_mno_fix_cortex_a53_843419, true)) {
std::string CPU = getCPUName(D, Args, Triple);
if (CPU.empty() || CPU == "generic" || CPU == "cortex-a53")
CmdArgs.push_back("--fix-cortex-a53-843419");
}
ToolChain.addExtraOpts(CmdArgs);
CmdArgs.push_back("--eh-frame-hdr");
if (const char *LDMOption = getLDMOption(ToolChain.getTriple(), Args)) {
CmdArgs.push_back("-m");
CmdArgs.push_back(LDMOption);
} else {
D.Diag(diag::err_target_unknown_triple) << Triple.str();
return;
}
if (Triple.isLoongArch() || Triple.isRISCV()) {
CmdArgs.push_back("-X");
if (Args.hasArg(options::OPT_mno_relax))
CmdArgs.push_back("--no-relax");
}
const bool IsShared = Args.hasArg(options::OPT_shared);
if (IsShared)
CmdArgs.push_back("-shared");
bool IsPIE = false;
if (IsStaticPIE) {
CmdArgs.push_back("-static");
CmdArgs.push_back("-pie");
CmdArgs.push_back("--no-dynamic-linker");
CmdArgs.push_back("-z");
CmdArgs.push_back("text");
} else if (IsStatic) {
CmdArgs.push_back("-static");
} else if (!Args.hasArg(options::OPT_r)) {
if (Args.hasArg(options::OPT_rdynamic))
CmdArgs.push_back("-export-dynamic");
if (!IsShared) {
IsPIE = Args.hasFlag(options::OPT_pie, options::OPT_no_pie,
ToolChain.isPIEDefault(Args));
if (IsPIE)
CmdArgs.push_back("-pie");
CmdArgs.push_back("-dynamic-linker");
CmdArgs.push_back(Args.MakeArgString(Twine(D.DyldPrefix) +
ToolChain.getDynamicLinker(Args)));
}
}
CmdArgs.push_back("-o");
CmdArgs.push_back(Output.getFilename());
if (!Args.hasArg(options::OPT_nostdlib, options::OPT_nostartfiles,
options::OPT_r)) {
if (!isAndroid && !IsIAMCU) {
const char *crt1 = nullptr;
if (!Args.hasArg(options::OPT_shared)) {
if (Args.hasArg(options::OPT_pg))
crt1 = "gcrt1.o";
else if (IsPIE)
crt1 = "Scrt1.o";
else if (IsStaticPIE)
crt1 = "rcrt1.o";
else
crt1 = "crt1.o";
}
if (crt1)
CmdArgs.push_back(Args.MakeArgString(ToolChain.GetFilePath(crt1)));
CmdArgs.push_back(Args.MakeArgString(ToolChain.GetFilePath("crti.o")));
}
if (IsVE) {
CmdArgs.push_back("-z");
CmdArgs.push_back("max-page-size=0x4000000");
}
if (IsIAMCU)
CmdArgs.push_back(Args.MakeArgString(ToolChain.GetFilePath("crt0.o")));
else if (HasCRTBeginEndFiles) {
std::string P;
if (ToolChain.GetRuntimeLibType(Args) == ToolChain::RLT_CompilerRT &&
!isAndroid) {
std::string crtbegin = ToolChain.getCompilerRT(Args, "crtbegin",
ToolChain::FT_Object);
if (ToolChain.getVFS().exists(crtbegin))
P = crtbegin;
}
if (P.empty()) {
const char *crtbegin;
if (Args.hasArg(options::OPT_shared))
crtbegin = isAndroid ? "crtbegin_so.o" : "crtbeginS.o";
else if (IsStatic)
crtbegin = isAndroid ? "crtbegin_static.o" : "crtbeginT.o";
else if (IsPIE || IsStaticPIE)
crtbegin = isAndroid ? "crtbegin_dynamic.o" : "crtbeginS.o";
else
crtbegin = isAndroid ? "crtbegin_dynamic.o" : "crtbegin.o";
P = ToolChain.GetFilePath(crtbegin);
}
CmdArgs.push_back(Args.MakeArgString(P));
}
// Add crtfastmath.o if available and fast math is enabled.
ToolChain.addFastMathRuntimeIfAvailable(Args, CmdArgs);
if (isAndroid && Args.hasFlag(options::OPT_fandroid_pad_segment,
options::OPT_fno_android_pad_segment, false))
CmdArgs.push_back(
Args.MakeArgString(ToolChain.GetFilePath("crt_pad_segment.o")));
}
Args.addAllArgs(CmdArgs, {options::OPT_L, options::OPT_u});
ToolChain.AddFilePathLibArgs(Args, CmdArgs);
if (D.isUsingLTO())
addLTOOptions(ToolChain, Args, CmdArgs, Output, Inputs,
D.getLTOMode() == LTOK_Thin);
if (Args.hasArg(options::OPT_Z_Xlinker__no_demangle))
CmdArgs.push_back("--no-demangle");
bool NeedsSanitizerDeps = addSanitizerRuntimes(ToolChain, Args, CmdArgs);
bool NeedsXRayDeps = addXRayRuntime(ToolChain, Args, CmdArgs);
addLinkerCompressDebugSectionsOption(ToolChain, Args, CmdArgs);
AddLinkerInputs(ToolChain, Inputs, Args, CmdArgs, JA);
addHIPRuntimeLibArgs(ToolChain, C, Args, CmdArgs);
// The profile runtime also needs access to system libraries.
getToolChain().addProfileRTLibs(Args, CmdArgs);
if (D.CCCIsCXX() &&
!Args.hasArg(options::OPT_nostdlib, options::OPT_nodefaultlibs,
options::OPT_r)) {
if (ToolChain.ShouldLinkCXXStdlib(Args)) {
bool OnlyLibstdcxxStatic = Args.hasArg(options::OPT_static_libstdcxx) &&
!Args.hasArg(options::OPT_static);
if (OnlyLibstdcxxStatic)
CmdArgs.push_back("-Bstatic");
ToolChain.AddCXXStdlibLibArgs(Args, CmdArgs);
if (OnlyLibstdcxxStatic)
CmdArgs.push_back("-Bdynamic");
}
CmdArgs.push_back("-lm");
}
// Silence warnings when linking C code with a C++ '-stdlib' argument.
Args.ClaimAllArgs(options::OPT_stdlib_EQ);
// Additional linker set-up and flags for Fortran. This is required in order
// to generate executables. As Fortran runtime depends on the C runtime,
// these dependencies need to be listed before the C runtime below (i.e.
// AddRunTimeLibs).
if (D.IsFlangMode() &&
!Args.hasArg(options::OPT_nostdlib, options::OPT_nodefaultlibs)) {
ToolChain.addFortranRuntimeLibraryPath(Args, CmdArgs);
ToolChain.addFortranRuntimeLibs(Args, CmdArgs);
CmdArgs.push_back("-lm");
}
if (!Args.hasArg(options::OPT_nostdlib, options::OPT_r)) {
if (!Args.hasArg(options::OPT_nodefaultlibs)) {
if (IsStatic || IsStaticPIE)
CmdArgs.push_back("--start-group");
if (NeedsSanitizerDeps)
linkSanitizerRuntimeDeps(ToolChain, Args, CmdArgs);
if (NeedsXRayDeps)
linkXRayRuntimeDeps(ToolChain, Args, CmdArgs);
bool WantPthread = Args.hasArg(options::OPT_pthread) ||
Args.hasArg(options::OPT_pthreads);
// Use the static OpenMP runtime with -static-openmp
bool StaticOpenMP = Args.hasArg(options::OPT_static_openmp) &&
!Args.hasArg(options::OPT_static);
// FIXME: Only pass GompNeedsRT = true for platforms with libgomp that
// require librt. Most modern Linux platforms do, but some may not.
if (addOpenMPRuntime(C, CmdArgs, ToolChain, Args, StaticOpenMP,
JA.isHostOffloading(Action::OFK_OpenMP),
/* GompNeedsRT= */ true))
// OpenMP runtimes implies pthreads when using the GNU toolchain.
// FIXME: Does this really make sense for all GNU toolchains?
WantPthread = true;
AddRunTimeLibs(ToolChain, D, CmdArgs, Args);
// LLVM support for atomics on 32-bit SPARC V8+ is incomplete, so
// forcibly link with libatomic as a workaround.
// TODO: Issue #41880 and D118021.
if (getToolChain().getTriple().getArch() == llvm::Triple::sparc) {
CmdArgs.push_back("--push-state");
CmdArgs.push_back("--as-needed");
CmdArgs.push_back("-latomic");
CmdArgs.push_back("--pop-state");
}
// We don't need libpthread neither for bionic (Android) nor for musl,
// (used by OHOS as runtime library).
if (WantPthread && !isAndroid && !isOHOSFamily)
CmdArgs.push_back("-lpthread");
if (Args.hasArg(options::OPT_fsplit_stack))
CmdArgs.push_back("--wrap=pthread_create");
if (!Args.hasArg(options::OPT_nolibc))
CmdArgs.push_back("-lc");
// Add IAMCU specific libs, if needed.
if (IsIAMCU)
CmdArgs.push_back("-lgloss");
if (IsStatic || IsStaticPIE)
CmdArgs.push_back("--end-group");
else
AddRunTimeLibs(ToolChain, D, CmdArgs, Args);
// Add IAMCU specific libs (outside the group), if needed.
if (IsIAMCU) {
CmdArgs.push_back("--as-needed");
CmdArgs.push_back("-lsoftfp");
CmdArgs.push_back("--no-as-needed");
}
}
if (!Args.hasArg(options::OPT_nostartfiles) && !IsIAMCU) {
if (HasCRTBeginEndFiles) {
std::string P;
if (ToolChain.GetRuntimeLibType(Args) == ToolChain::RLT_CompilerRT &&
!isAndroid) {
std::string crtend = ToolChain.getCompilerRT(Args, "crtend",
ToolChain::FT_Object);
if (ToolChain.getVFS().exists(crtend))
P = crtend;
}
if (P.empty()) {
const char *crtend;
if (Args.hasArg(options::OPT_shared))
crtend = isAndroid ? "crtend_so.o" : "crtendS.o";
else if (IsPIE || IsStaticPIE)
crtend = isAndroid ? "crtend_android.o" : "crtendS.o";
else
crtend = isAndroid ? "crtend_android.o" : "crtend.o";
P = ToolChain.GetFilePath(crtend);
}
CmdArgs.push_back(Args.MakeArgString(P));
}
if (!isAndroid)
CmdArgs.push_back(Args.MakeArgString(ToolChain.GetFilePath("crtn.o")));
}
}
Args.addAllArgs(CmdArgs, {options::OPT_T, options::OPT_t});
const char *Exec = Args.MakeArgString(ToolChain.GetLinkerPath());
C.addCommand(std::make_unique<Command>(JA, *this,
ResponseFileSupport::AtFileCurCP(),
Exec, CmdArgs, Inputs, Output));
}
void tools::gnutools::Assembler::ConstructJob(Compilation &C,
const JobAction &JA,
const InputInfo &Output,
const InputInfoList &Inputs,
const ArgList &Args,
const char *LinkingOutput) const {
const auto &D = getToolChain().getDriver();
claimNoWarnArgs(Args);
ArgStringList CmdArgs;
llvm::Reloc::Model RelocationModel;
unsigned PICLevel;
bool IsPIE;
const char *DefaultAssembler = "as";
// Enforce GNU as on Solaris; the native assembler's input syntax isn't fully
// compatible.
if (getToolChain().getTriple().isOSSolaris())
DefaultAssembler = "gas";
std::tie(RelocationModel, PICLevel, IsPIE) =
ParsePICArgs(getToolChain(), Args);
if (const Arg *A = Args.getLastArg(options::OPT_gz, options::OPT_gz_EQ)) {
if (A->getOption().getID() == options::OPT_gz) {
CmdArgs.push_back("--compress-debug-sections");
} else {
StringRef Value = A->getValue();
if (Value == "none" || Value == "zlib" || Value == "zstd") {
CmdArgs.push_back(
Args.MakeArgString("--compress-debug-sections=" + Twine(Value)));
} else {
D.Diag(diag::err_drv_unsupported_option_argument)
<< A->getSpelling() << Value;
}
}
}
switch (getToolChain().getArch()) {
default:
break;
// Add --32/--64 to make sure we get the format we want.
// This is incomplete
case llvm::Triple::x86:
CmdArgs.push_back("--32");
break;
case llvm::Triple::x86_64:
if (getToolChain().getTriple().isX32())
CmdArgs.push_back("--x32");
else
CmdArgs.push_back("--64");
break;
case llvm::Triple::ppc: {
CmdArgs.push_back("-a32");
CmdArgs.push_back("-mppc");
CmdArgs.push_back("-mbig-endian");
CmdArgs.push_back(ppc::getPPCAsmModeForCPU(
getCPUName(D, Args, getToolChain().getTriple())));
break;
}
case llvm::Triple::ppcle: {
CmdArgs.push_back("-a32");
CmdArgs.push_back("-mppc");
CmdArgs.push_back("-mlittle-endian");
CmdArgs.push_back(ppc::getPPCAsmModeForCPU(
getCPUName(D, Args, getToolChain().getTriple())));
break;
}
case llvm::Triple::ppc64: {
CmdArgs.push_back("-a64");
CmdArgs.push_back("-mppc64");
CmdArgs.push_back("-mbig-endian");
CmdArgs.push_back(ppc::getPPCAsmModeForCPU(
getCPUName(D, Args, getToolChain().getTriple())));
break;
}
case llvm::Triple::ppc64le: {
CmdArgs.push_back("-a64");
CmdArgs.push_back("-mppc64");
CmdArgs.push_back("-mlittle-endian");
CmdArgs.push_back(ppc::getPPCAsmModeForCPU(
getCPUName(D, Args, getToolChain().getTriple())));
break;
}
case llvm::Triple::riscv32:
case llvm::Triple::riscv64: {
StringRef ABIName = riscv::getRISCVABI(Args, getToolChain().getTriple());
CmdArgs.push_back("-mabi");
CmdArgs.push_back(ABIName.data());
std::string MArchName =
riscv::getRISCVArch(Args, getToolChain().getTriple());
CmdArgs.push_back("-march");
CmdArgs.push_back(Args.MakeArgString(MArchName));
if (!Args.hasFlag(options::OPT_mrelax, options::OPT_mno_relax, true))
Args.addOptOutFlag(CmdArgs, options::OPT_mrelax, options::OPT_mno_relax);
break;
}
case llvm::Triple::sparc:
case llvm::Triple::sparcel: {
CmdArgs.push_back("-32");
std::string CPU = getCPUName(D, Args, getToolChain().getTriple());
CmdArgs.push_back(
sparc::getSparcAsmModeForCPU(CPU, getToolChain().getTriple()));
AddAssemblerKPIC(getToolChain(), Args, CmdArgs);
break;
}
case llvm::Triple::sparcv9: {
CmdArgs.push_back("-64");
std::string CPU = getCPUName(D, Args, getToolChain().getTriple());
CmdArgs.push_back(
sparc::getSparcAsmModeForCPU(CPU, getToolChain().getTriple()));
AddAssemblerKPIC(getToolChain(), Args, CmdArgs);
break;
}
case llvm::Triple::arm:
case llvm::Triple::armeb:
case llvm::Triple::thumb:
case llvm::Triple::thumbeb: {
const llvm::Triple &Triple2 = getToolChain().getTriple();
CmdArgs.push_back(arm::isARMBigEndian(Triple2, Args) ? "-EB" : "-EL");
switch (Triple2.getSubArch()) {
case llvm::Triple::ARMSubArch_v7:
CmdArgs.push_back("-mfpu=neon");
break;
case llvm::Triple::ARMSubArch_v8:
CmdArgs.push_back("-mfpu=crypto-neon-fp-armv8");
break;
default:
break;
}
switch (arm::getARMFloatABI(getToolChain(), Args)) {
case arm::FloatABI::Invalid: llvm_unreachable("must have an ABI!");
case arm::FloatABI::Soft:
CmdArgs.push_back(Args.MakeArgString("-mfloat-abi=soft"));
break;
case arm::FloatABI::SoftFP:
CmdArgs.push_back(Args.MakeArgString("-mfloat-abi=softfp"));
break;
case arm::FloatABI::Hard:
CmdArgs.push_back(Args.MakeArgString("-mfloat-abi=hard"));
break;
}
Args.AddLastArg(CmdArgs, options::OPT_march_EQ);
normalizeCPUNamesForAssembler(Args, CmdArgs);
Args.AddLastArg(CmdArgs, options::OPT_mfpu_EQ);
// The integrated assembler doesn't implement e_flags setting behavior for
// -meabi=gnu (gcc -mabi={apcs-gnu,atpcs} passes -meabi=gnu to gas). For
// compatibility we accept but warn.
if (Arg *A = Args.getLastArgNoClaim(options::OPT_mabi_EQ))
A->ignoreTargetSpecific();
break;
}
case llvm::Triple::aarch64:
case llvm::Triple::aarch64_be: {
CmdArgs.push_back(
getToolChain().getArch() == llvm::Triple::aarch64_be ? "-EB" : "-EL");
Args.AddLastArg(CmdArgs, options::OPT_march_EQ);
normalizeCPUNamesForAssembler(Args, CmdArgs);
break;
}
// TODO: handle loongarch32.
case llvm::Triple::loongarch64: {
StringRef ABIName =
loongarch::getLoongArchABI(D, Args, getToolChain().getTriple());
CmdArgs.push_back(Args.MakeArgString("-mabi=" + ABIName));
break;
}
case llvm::Triple::mips:
case llvm::Triple::mipsel:
case llvm::Triple::mips64:
case llvm::Triple::mips64el: {
StringRef CPUName;
StringRef ABIName;
mips::getMipsCPUAndABI(Args, getToolChain().getTriple(), CPUName, ABIName);
ABIName = mips::getGnuCompatibleMipsABIName(ABIName);
CmdArgs.push_back("-march");
CmdArgs.push_back(CPUName.data());
CmdArgs.push_back("-mabi");
CmdArgs.push_back(ABIName.data());
// -mno-shared should be emitted unless -fpic, -fpie, -fPIC, -fPIE,
// or -mshared (not implemented) is in effect.
if (RelocationModel == llvm::Reloc::Static)
CmdArgs.push_back("-mno-shared");
// LLVM doesn't support -mplt yet and acts as if it is always given.
// However, -mplt has no effect with the N64 ABI.
if (ABIName != "64" && !Args.hasArg(options::OPT_mno_abicalls))
CmdArgs.push_back("-call_nonpic");
if (getToolChain().getTriple().isLittleEndian())
CmdArgs.push_back("-EL");
else
CmdArgs.push_back("-EB");
if (Arg *A = Args.getLastArg(options::OPT_mnan_EQ)) {
if (StringRef(A->getValue()) == "2008")
CmdArgs.push_back(Args.MakeArgString("-mnan=2008"));
}
// Add the last -mfp32/-mfpxx/-mfp64 or -mfpxx if it is enabled by default.
if (Arg *A = Args.getLastArg(options::OPT_mfp32, options::OPT_mfpxx,
options::OPT_mfp64)) {
A->claim();
A->render(Args, CmdArgs);
} else if (mips::shouldUseFPXX(
Args, getToolChain().getTriple(), CPUName, ABIName,
mips::getMipsFloatABI(getToolChain().getDriver(), Args,
getToolChain().getTriple())))
CmdArgs.push_back("-mfpxx");
// Pass on -mmips16 or -mno-mips16. However, the assembler equivalent of
// -mno-mips16 is actually -no-mips16.
if (Arg *A =
Args.getLastArg(options::OPT_mips16, options::OPT_mno_mips16)) {
if (A->getOption().matches(options::OPT_mips16)) {
A->claim();
A->render(Args, CmdArgs);
} else {
A->claim();
CmdArgs.push_back("-no-mips16");
}
}
Args.AddLastArg(CmdArgs, options::OPT_mmicromips,
options::OPT_mno_micromips);
Args.AddLastArg(CmdArgs, options::OPT_mdsp, options::OPT_mno_dsp);
Args.AddLastArg(CmdArgs, options::OPT_mdspr2, options::OPT_mno_dspr2);
if (Arg *A = Args.getLastArg(options::OPT_mmsa, options::OPT_mno_msa)) {
// Do not use AddLastArg because not all versions of MIPS assembler
// support -mmsa / -mno-msa options.
if (A->getOption().matches(options::OPT_mmsa))
CmdArgs.push_back(Args.MakeArgString("-mmsa"));
}
Args.AddLastArg(CmdArgs, options::OPT_mhard_float,
options::OPT_msoft_float);
Args.AddLastArg(CmdArgs, options::OPT_mdouble_float,
options::OPT_msingle_float);
Args.AddLastArg(CmdArgs, options::OPT_modd_spreg,
options::OPT_mno_odd_spreg);
AddAssemblerKPIC(getToolChain(), Args, CmdArgs);
break;
}
case llvm::Triple::systemz: {
// Always pass an -march option, since our default of z10 is later
// than the GNU assembler's default.
std::string CPUName =
systemz::getSystemZTargetCPU(Args, getToolChain().getTriple());
CmdArgs.push_back(Args.MakeArgString("-march=" + CPUName));
break;
}
case llvm::Triple::ve:
DefaultAssembler = "nas";
}
for (const Arg *A : Args.filtered(options::OPT_ffile_prefix_map_EQ,
options::OPT_fdebug_prefix_map_EQ)) {
StringRef Map = A->getValue();
if (!Map.contains('='))
D.Diag(diag::err_drv_invalid_argument_to_option)
<< Map << A->getOption().getName();
else {
CmdArgs.push_back(Args.MakeArgString("--debug-prefix-map"));
CmdArgs.push_back(Args.MakeArgString(Map));
}
A->claim();
}
Args.AddAllArgs(CmdArgs, options::OPT_I);
Args.AddAllArgValues(CmdArgs, options::OPT_Wa_COMMA, options::OPT_Xassembler);
CmdArgs.push_back("-o");
CmdArgs.push_back(Output.getFilename());
for (const auto &II : Inputs)
CmdArgs.push_back(II.getFilename());
if (Arg *A = Args.getLastArg(options::OPT_g_Flag, options::OPT_gN_Group,
options::OPT_gdwarf_2, options::OPT_gdwarf_3,
options::OPT_gdwarf_4, options::OPT_gdwarf_5,
options::OPT_gdwarf))
if (!A->getOption().matches(options::OPT_g0)) {
Args.AddLastArg(CmdArgs, options::OPT_g_Flag);
unsigned DwarfVersion = getDwarfVersion(getToolChain(), Args);
CmdArgs.push_back(Args.MakeArgString("-gdwarf-" + Twine(DwarfVersion)));
}
const char *Exec =
Args.MakeArgString(getToolChain().GetProgramPath(DefaultAssembler));
C.addCommand(std::make_unique<Command>(JA, *this,
ResponseFileSupport::AtFileCurCP(),
Exec, CmdArgs, Inputs, Output));
// Handle the debug info splitting at object creation time if we're
// creating an object.
// TODO: Currently only works on linux with newer objcopy.
if (Args.hasArg(options::OPT_gsplit_dwarf) &&
getToolChain().getTriple().isOSLinux())
SplitDebugInfo(getToolChain(), C, *this, JA, Args, Output,
SplitDebugName(JA, Args, Inputs[0], Output));
}
namespace {
// Filter to remove Multilibs that don't exist as a suffix to Path
class FilterNonExistent {
StringRef Base, File;
llvm::vfs::FileSystem &VFS;
public:
FilterNonExistent(StringRef Base, StringRef File, llvm::vfs::FileSystem &VFS)
: Base(Base), File(File), VFS(VFS) {}
bool operator()(const Multilib &M) {
return !VFS.exists(Base + M.gccSuffix() + File);
}
};
} // end anonymous namespace
static bool isSoftFloatABI(const ArgList &Args) {
Arg *A = Args.getLastArg(options::OPT_msoft_float, options::OPT_mhard_float,
options::OPT_mfloat_abi_EQ);
if (!A)
return false;
return A->getOption().matches(options::OPT_msoft_float) ||
(A->getOption().matches(options::OPT_mfloat_abi_EQ) &&
A->getValue() == StringRef("soft"));
}
static bool isArmOrThumbArch(llvm::Triple::ArchType Arch) {
return Arch == llvm::Triple::arm || Arch == llvm::Triple::thumb;
}
static bool isMipsEL(llvm::Triple::ArchType Arch) {
return Arch == llvm::Triple::mipsel || Arch == llvm::Triple::mips64el;
}
static bool isMips16(const ArgList &Args) {
Arg *A = Args.getLastArg(options::OPT_mips16, options::OPT_mno_mips16);
return A && A->getOption().matches(options::OPT_mips16);
}
static bool isMicroMips(const ArgList &Args) {
Arg *A = Args.getLastArg(options::OPT_mmicromips, options::OPT_mno_micromips);
return A && A->getOption().matches(options::OPT_mmicromips);
}
static bool isMSP430(llvm::Triple::ArchType Arch) {
return Arch == llvm::Triple::msp430;
}
static bool findMipsCsMultilibs(const Driver &D,
const Multilib::flags_list &Flags,
FilterNonExistent &NonExistent,
DetectedMultilibs &Result) {
// Check for Code Sourcery toolchain multilibs
MultilibSet CSMipsMultilibs;
{
auto MArchMips16 = MultilibBuilder("/mips16").flag("-m32").flag("-mips16");
auto MArchMicroMips =
MultilibBuilder("/micromips").flag("-m32").flag("-mmicromips");
auto MArchDefault = MultilibBuilder("")
.flag("-mips16", /*Disallow=*/true)
.flag("-mmicromips", /*Disallow=*/true);
auto UCLibc = MultilibBuilder("/uclibc").flag("-muclibc");
auto SoftFloat = MultilibBuilder("/soft-float").flag("-msoft-float");
auto Nan2008 = MultilibBuilder("/nan2008").flag("-mnan=2008");
auto DefaultFloat = MultilibBuilder("")
.flag("-msoft-float", /*Disallow=*/true)
.flag("-mnan=2008", /*Disallow=*/true);
auto BigEndian =
MultilibBuilder("").flag("-EB").flag("-EL", /*Disallow=*/true);
auto LittleEndian =
MultilibBuilder("/el").flag("-EL").flag("-EB", /*Disallow=*/true);
// Note that this one's osSuffix is ""
auto MAbi64 = MultilibBuilder("")
.gccSuffix("/64")
.includeSuffix("/64")
.flag("-mabi=n64")
.flag("-mabi=n32", /*Disallow=*/true)
.flag("-m32", /*Disallow=*/true);
CSMipsMultilibs =
MultilibSetBuilder()
.Either(MArchMips16, MArchMicroMips, MArchDefault)
.Maybe(UCLibc)
.Either(SoftFloat, Nan2008, DefaultFloat)
.FilterOut("/micromips/nan2008")
.FilterOut("/mips16/nan2008")
.Either(BigEndian, LittleEndian)
.Maybe(MAbi64)
.FilterOut("/mips16.*/64")
.FilterOut("/micromips.*/64")
.makeMultilibSet()
.FilterOut(NonExistent)
.setIncludeDirsCallback([](const Multilib &M) {
std::vector<std::string> Dirs({"/include"});
if (StringRef(M.includeSuffix()).starts_with("/uclibc"))
Dirs.push_back(
"/../../../../mips-linux-gnu/libc/uclibc/usr/include");
else
Dirs.push_back("/../../../../mips-linux-gnu/libc/usr/include");
return Dirs;
});
}
MultilibSet DebianMipsMultilibs;
{
MultilibBuilder MAbiN32 =
MultilibBuilder().gccSuffix("/n32").includeSuffix("/n32").flag(
"-mabi=n32");
MultilibBuilder M64 = MultilibBuilder()
.gccSuffix("/64")
.includeSuffix("/64")
.flag("-m64")
.flag("-m32", /*Disallow=*/true)
.flag("-mabi=n32", /*Disallow=*/true);
MultilibBuilder M32 = MultilibBuilder()
.gccSuffix("/32")
.flag("-m64", /*Disallow=*/true)
.flag("-m32")
.flag("-mabi=n32", /*Disallow=*/true);
DebianMipsMultilibs = MultilibSetBuilder()
.Either(M32, M64, MAbiN32)
.makeMultilibSet()
.FilterOut(NonExistent);
}
// Sort candidates. Toolchain that best meets the directories tree goes first.
// Then select the first toolchains matches command line flags.
MultilibSet *Candidates[] = {&CSMipsMultilibs, &DebianMipsMultilibs};
if (CSMipsMultilibs.size() < DebianMipsMultilibs.size())
std::iter_swap(Candidates, Candidates + 1);
for (const MultilibSet *Candidate : Candidates) {
if (Candidate->select(D, Flags, Result.SelectedMultilibs)) {
if (Candidate == &DebianMipsMultilibs)
Result.BiarchSibling = Multilib();
Result.Multilibs = *Candidate;
return true;
}
}
return false;
}
static bool findMipsMuslMultilibs(const Driver &D,
const Multilib::flags_list &Flags,
FilterNonExistent &NonExistent,
DetectedMultilibs &Result) {
// Musl toolchain multilibs
MultilibSet MuslMipsMultilibs;
{
auto MArchMipsR2 = MultilibBuilder("")
.osSuffix("/mips-r2-hard-musl")
.flag("-EB")
.flag("-EL", /*Disallow=*/true)
.flag("-march=mips32r2");
auto MArchMipselR2 = MultilibBuilder("/mipsel-r2-hard-musl")
.flag("-EB", /*Disallow=*/true)
.flag("-EL")
.flag("-march=mips32r2");
MuslMipsMultilibs = MultilibSetBuilder()
.Either(MArchMipsR2, MArchMipselR2)
.makeMultilibSet();
// Specify the callback that computes the include directories.
MuslMipsMultilibs.setIncludeDirsCallback([](const Multilib &M) {
return std::vector<std::string>(
{"/../sysroot" + M.osSuffix() + "/usr/include"});
});
}
if (MuslMipsMultilibs.select(D, Flags, Result.SelectedMultilibs)) {
Result.Multilibs = MuslMipsMultilibs;
return true;
}
return false;
}
static bool findMipsMtiMultilibs(const Driver &D,
const Multilib::flags_list &Flags,
FilterNonExistent &NonExistent,
DetectedMultilibs &Result) {
// CodeScape MTI toolchain v1.2 and early.
MultilibSet MtiMipsMultilibsV1;
{
auto MArchMips32 = MultilibBuilder("/mips32")
.flag("-m32")
.flag("-m64", /*Disallow=*/true)
.flag("-mmicromips", /*Disallow=*/true)
.flag("-march=mips32");
auto MArchMicroMips = MultilibBuilder("/micromips")
.flag("-m32")
.flag("-m64", /*Disallow=*/true)
.flag("-mmicromips");
auto MArchMips64r2 = MultilibBuilder("/mips64r2")
.flag("-m32", /*Disallow=*/true)
.flag("-m64")
.flag("-march=mips64r2");
auto MArchMips64 = MultilibBuilder("/mips64")
.flag("-m32", /*Disallow=*/true)
.flag("-m64")
.flag("-march=mips64r2", /*Disallow=*/true);
auto MArchDefault = MultilibBuilder("")
.flag("-m32")
.flag("-m64", /*Disallow=*/true)
.flag("-mmicromips", /*Disallow=*/true)
.flag("-march=mips32r2");
auto Mips16 = MultilibBuilder("/mips16").flag("-mips16");
auto UCLibc = MultilibBuilder("/uclibc").flag("-muclibc");
auto MAbi64 = MultilibBuilder("/64")
.flag("-mabi=n64")
.flag("-mabi=n32", /*Disallow=*/true)
.flag("-m32", /*Disallow=*/true);
auto BigEndian =
MultilibBuilder("").flag("-EB").flag("-EL", /*Disallow=*/true);
auto LittleEndian =
MultilibBuilder("/el").flag("-EL").flag("-EB", /*Disallow=*/true);
auto SoftFloat = MultilibBuilder("/sof").flag("-msoft-float");
auto Nan2008 = MultilibBuilder("/nan2008").flag("-mnan=2008");
MtiMipsMultilibsV1 =
MultilibSetBuilder()
.Either(MArchMips32, MArchMicroMips, MArchMips64r2, MArchMips64,
MArchDefault)
.Maybe(UCLibc)
.Maybe(Mips16)
.FilterOut("/mips64/mips16")
.FilterOut("/mips64r2/mips16")
.FilterOut("/micromips/mips16")
.Maybe(MAbi64)
.FilterOut("/micromips/64")
.FilterOut("/mips32/64")
.FilterOut("^/64")
.FilterOut("/mips16/64")
.Either(BigEndian, LittleEndian)
.Maybe(SoftFloat)
.Maybe(Nan2008)
.FilterOut(".*sof/nan2008")
.makeMultilibSet()
.FilterOut(NonExistent)
.setIncludeDirsCallback([](const Multilib &M) {
std::vector<std::string> Dirs({"/include"});
if (StringRef(M.includeSuffix()).starts_with("/uclibc"))
Dirs.push_back("/../../../../sysroot/uclibc/usr/include");
else
Dirs.push_back("/../../../../sysroot/usr/include");
return Dirs;
});
}
// CodeScape IMG toolchain starting from v1.3.
MultilibSet MtiMipsMultilibsV2;
{
auto BeHard = MultilibBuilder("/mips-r2-hard")
.flag("-EB")
.flag("-msoft-float", /*Disallow=*/true)
.flag("-mnan=2008", /*Disallow=*/true)
.flag("-muclibc", /*Disallow=*/true);
auto BeSoft = MultilibBuilder("/mips-r2-soft")
.flag("-EB")
.flag("-msoft-float")
.flag("-mnan=2008", /*Disallow=*/true);
auto ElHard = MultilibBuilder("/mipsel-r2-hard")
.flag("-EL")
.flag("-msoft-float", /*Disallow=*/true)
.flag("-mnan=2008", /*Disallow=*/true)
.flag("-muclibc", /*Disallow=*/true);
auto ElSoft = MultilibBuilder("/mipsel-r2-soft")
.flag("-EL")
.flag("-msoft-float")
.flag("-mnan=2008", /*Disallow=*/true)
.flag("-mmicromips", /*Disallow=*/true);
auto BeHardNan = MultilibBuilder("/mips-r2-hard-nan2008")
.flag("-EB")
.flag("-msoft-float", /*Disallow=*/true)
.flag("-mnan=2008")
.flag("-muclibc", /*Disallow=*/true);
auto ElHardNan = MultilibBuilder("/mipsel-r2-hard-nan2008")
.flag("-EL")
.flag("-msoft-float", /*Disallow=*/true)
.flag("-mnan=2008")
.flag("-muclibc", /*Disallow=*/true)
.flag("-mmicromips", /*Disallow=*/true);
auto BeHardNanUclibc = MultilibBuilder("/mips-r2-hard-nan2008-uclibc")
.flag("-EB")
.flag("-msoft-float", /*Disallow=*/true)
.flag("-mnan=2008")
.flag("-muclibc");
auto ElHardNanUclibc = MultilibBuilder("/mipsel-r2-hard-nan2008-uclibc")
.flag("-EL")
.flag("-msoft-float", /*Disallow=*/true)
.flag("-mnan=2008")
.flag("-muclibc");
auto BeHardUclibc = MultilibBuilder("/mips-r2-hard-uclibc")
.flag("-EB")
.flag("-msoft-float", /*Disallow=*/true)
.flag("-mnan=2008", /*Disallow=*/true)
.flag("-muclibc");
auto ElHardUclibc = MultilibBuilder("/mipsel-r2-hard-uclibc")
.flag("-EL")
.flag("-msoft-float", /*Disallow=*/true)
.flag("-mnan=2008", /*Disallow=*/true)
.flag("-muclibc");
auto ElMicroHardNan = MultilibBuilder("/micromipsel-r2-hard-nan2008")
.flag("-EL")
.flag("-msoft-float", /*Disallow=*/true)
.flag("-mnan=2008")
.flag("-mmicromips");
auto ElMicroSoft = MultilibBuilder("/micromipsel-r2-soft")
.flag("-EL")
.flag("-msoft-float")
.flag("-mnan=2008", /*Disallow=*/true)
.flag("-mmicromips");
auto O32 = MultilibBuilder("/lib")
.osSuffix("")
.flag("-mabi=n32", /*Disallow=*/true)
.flag("-mabi=n64", /*Disallow=*/true);
auto N32 = MultilibBuilder("/lib32")
.osSuffix("")
.flag("-mabi=n32")
.flag("-mabi=n64", /*Disallow=*/true);
auto N64 = MultilibBuilder("/lib64")
.osSuffix("")
.flag("-mabi=n32", /*Disallow=*/true)
.flag("-mabi=n64");
MtiMipsMultilibsV2 =
MultilibSetBuilder()
.Either({BeHard, BeSoft, ElHard, ElSoft, BeHardNan, ElHardNan,
BeHardNanUclibc, ElHardNanUclibc, BeHardUclibc,
ElHardUclibc, ElMicroHardNan, ElMicroSoft})
.Either(O32, N32, N64)
.makeMultilibSet()
.FilterOut(NonExistent)
.setIncludeDirsCallback([](const Multilib &M) {
return std::vector<std::string>({"/../../../../sysroot" +
M.includeSuffix() +
"/../usr/include"});
})
.setFilePathsCallback([](const Multilib &M) {
return std::vector<std::string>(
{"/../../../../mips-mti-linux-gnu/lib" + M.gccSuffix()});
});
}
for (auto *Candidate : {&MtiMipsMultilibsV1, &MtiMipsMultilibsV2}) {
if (Candidate->select(D, Flags, Result.SelectedMultilibs)) {
Result.Multilibs = *Candidate;
return true;
}
}
return false;
}
static bool findMipsImgMultilibs(const Driver &D,
const Multilib::flags_list &Flags,
FilterNonExistent &NonExistent,
DetectedMultilibs &Result) {
// CodeScape IMG toolchain v1.2 and early.
MultilibSet ImgMultilibsV1;
{
auto Mips64r6 = MultilibBuilder("/mips64r6")
.flag("-m64")
.flag("-m32", /*Disallow=*/true);
auto LittleEndian =
MultilibBuilder("/el").flag("-EL").flag("-EB", /*Disallow=*/true);
auto MAbi64 = MultilibBuilder("/64")
.flag("-mabi=n64")
.flag("-mabi=n32", /*Disallow=*/true)
.flag("-m32", /*Disallow=*/true);
ImgMultilibsV1 =
MultilibSetBuilder()
.Maybe(Mips64r6)
.Maybe(MAbi64)
.Maybe(LittleEndian)
.makeMultilibSet()
.FilterOut(NonExistent)
.setIncludeDirsCallback([](const Multilib &M) {
return std::vector<std::string>(
{"/include", "/../../../../sysroot/usr/include"});
});
}
// CodeScape IMG toolchain starting from v1.3.
MultilibSet ImgMultilibsV2;
{
auto BeHard = MultilibBuilder("/mips-r6-hard")
.flag("-EB")
.flag("-msoft-float", /*Disallow=*/true)
.flag("-mmicromips", /*Disallow=*/true);
auto BeSoft = MultilibBuilder("/mips-r6-soft")
.flag("-EB")
.flag("-msoft-float")
.flag("-mmicromips", /*Disallow=*/true);
auto ElHard = MultilibBuilder("/mipsel-r6-hard")
.flag("-EL")
.flag("-msoft-float", /*Disallow=*/true)
.flag("-mmicromips", /*Disallow=*/true);
auto ElSoft = MultilibBuilder("/mipsel-r6-soft")
.flag("-EL")
.flag("-msoft-float")
.flag("-mmicromips", /*Disallow=*/true);
auto BeMicroHard = MultilibBuilder("/micromips-r6-hard")
.flag("-EB")
.flag("-msoft-float", /*Disallow=*/true)
.flag("-mmicromips");
auto BeMicroSoft = MultilibBuilder("/micromips-r6-soft")
.flag("-EB")
.flag("-msoft-float")
.flag("-mmicromips");
auto ElMicroHard = MultilibBuilder("/micromipsel-r6-hard")
.flag("-EL")
.flag("-msoft-float", /*Disallow=*/true)
.flag("-mmicromips");
auto ElMicroSoft = MultilibBuilder("/micromipsel-r6-soft")
.flag("-EL")
.flag("-msoft-float")
.flag("-mmicromips");
auto O32 = MultilibBuilder("/lib")
.osSuffix("")
.flag("-mabi=n32", /*Disallow=*/true)
.flag("-mabi=n64", /*Disallow=*/true);
auto N32 = MultilibBuilder("/lib32")
.osSuffix("")
.flag("-mabi=n32")
.flag("-mabi=n64", /*Disallow=*/true);
auto N64 = MultilibBuilder("/lib64")
.osSuffix("")
.flag("-mabi=n32", /*Disallow=*/true)
.flag("-mabi=n64");
ImgMultilibsV2 =
MultilibSetBuilder()
.Either({BeHard, BeSoft, ElHard, ElSoft, BeMicroHard, BeMicroSoft,
ElMicroHard, ElMicroSoft})
.Either(O32, N32, N64)
.makeMultilibSet()
.FilterOut(NonExistent)
.setIncludeDirsCallback([](const Multilib &M) {
return std::vector<std::string>({"/../../../../sysroot" +
M.includeSuffix() +
"/../usr/include"});
})
.setFilePathsCallback([](const Multilib &M) {
return std::vector<std::string>(
{"/../../../../mips-img-linux-gnu/lib" + M.gccSuffix()});
});
}
for (auto *Candidate : {&ImgMultilibsV1, &ImgMultilibsV2}) {
if (Candidate->select(D, Flags, Result.SelectedMultilibs)) {
Result.Multilibs = *Candidate;
return true;
}
}
return false;
}
bool clang::driver::findMIPSMultilibs(const Driver &D,
const llvm::Triple &TargetTriple,
StringRef Path, const ArgList &Args,
DetectedMultilibs &Result) {
FilterNonExistent NonExistent(Path, "/crtbegin.o", D.getVFS());
StringRef CPUName;
StringRef ABIName;
tools::mips::getMipsCPUAndABI(Args, TargetTriple, CPUName, ABIName);
llvm::Triple::ArchType TargetArch = TargetTriple.getArch();
Multilib::flags_list Flags;
addMultilibFlag(TargetTriple.isMIPS32(), "-m32", Flags);
addMultilibFlag(TargetTriple.isMIPS64(), "-m64", Flags);
addMultilibFlag(isMips16(Args), "-mips16", Flags);
addMultilibFlag(CPUName == "mips32", "-march=mips32", Flags);
addMultilibFlag(CPUName == "mips32r2" || CPUName == "mips32r3" ||
CPUName == "mips32r5" || CPUName == "p5600",
"-march=mips32r2", Flags);
addMultilibFlag(CPUName == "mips32r6", "-march=mips32r6", Flags);
addMultilibFlag(CPUName == "mips64", "-march=mips64", Flags);
addMultilibFlag(CPUName == "mips64r2" || CPUName == "mips64r3" ||
CPUName == "mips64r5" || CPUName == "octeon" ||
CPUName == "octeon+",
"-march=mips64r2", Flags);
addMultilibFlag(CPUName == "mips64r6" || CPUName == "i6400" ||
CPUName == "i6500",
"-march=mips64r6", Flags);
addMultilibFlag(isMicroMips(Args), "-mmicromips", Flags);
addMultilibFlag(tools::mips::isUCLibc(Args), "-muclibc", Flags);
addMultilibFlag(tools::mips::isNaN2008(D, Args, TargetTriple), "-mnan=2008",
Flags);
addMultilibFlag(ABIName == "n32", "-mabi=n32", Flags);
addMultilibFlag(ABIName == "n64", "-mabi=n64", Flags);
addMultilibFlag(isSoftFloatABI(Args), "-msoft-float", Flags);
addMultilibFlag(!isSoftFloatABI(Args), "-mhard-float", Flags);
addMultilibFlag(isMipsEL(TargetArch), "-EL", Flags);
addMultilibFlag(!isMipsEL(TargetArch), "-EB", Flags);
if (TargetTriple.getVendor() == llvm::Triple::MipsTechnologies &&
TargetTriple.getOS() == llvm::Triple::Linux &&
TargetTriple.getEnvironment() == llvm::Triple::UnknownEnvironment)
return findMipsMuslMultilibs(D, Flags, NonExistent, Result);
if (TargetTriple.getVendor() == llvm::Triple::MipsTechnologies &&
TargetTriple.getOS() == llvm::Triple::Linux &&
TargetTriple.isGNUEnvironment())
return findMipsMtiMultilibs(D, Flags, NonExistent, Result);
if (TargetTriple.getVendor() == llvm::Triple::ImaginationTechnologies &&
TargetTriple.getOS() == llvm::Triple::Linux &&
TargetTriple.isGNUEnvironment())
return findMipsImgMultilibs(D, Flags, NonExistent, Result);
if (findMipsCsMultilibs(D, Flags, NonExistent, Result))
return true;
// Fallback to the regular toolchain-tree structure.
Multilib Default;
Result.Multilibs.push_back(Default);
Result.Multilibs.FilterOut(NonExistent);
if (Result.Multilibs.select(D, Flags, Result.SelectedMultilibs)) {
Result.BiarchSibling = Multilib();
return true;
}
return false;
}
static void findAndroidArmMultilibs(const Driver &D,
const llvm::Triple &TargetTriple,
StringRef Path, const ArgList &Args,
DetectedMultilibs &Result) {
// Find multilibs with subdirectories like armv7-a, thumb, armv7-a/thumb.
FilterNonExistent NonExistent(Path, "/crtbegin.o", D.getVFS());
MultilibBuilder ArmV7Multilib = MultilibBuilder("/armv7-a")
.flag("-march=armv7-a")
.flag("-mthumb", /*Disallow=*/true);
MultilibBuilder ThumbMultilib = MultilibBuilder("/thumb")
.flag("-march=armv7-a", /*Disallow=*/true)
.flag("-mthumb");
MultilibBuilder ArmV7ThumbMultilib =
MultilibBuilder("/armv7-a/thumb").flag("-march=armv7-a").flag("-mthumb");
MultilibBuilder DefaultMultilib =
MultilibBuilder("")
.flag("-march=armv7-a", /*Disallow=*/true)
.flag("-mthumb", /*Disallow=*/true);
MultilibSet AndroidArmMultilibs =
MultilibSetBuilder()
.Either(ThumbMultilib, ArmV7Multilib, ArmV7ThumbMultilib,
DefaultMultilib)
.makeMultilibSet()
.FilterOut(NonExistent);
Multilib::flags_list Flags;
llvm::StringRef Arch = Args.getLastArgValue(options::OPT_march_EQ);
bool IsArmArch = TargetTriple.getArch() == llvm::Triple::arm;
bool IsThumbArch = TargetTriple.getArch() == llvm::Triple::thumb;
bool IsV7SubArch = TargetTriple.getSubArch() == llvm::Triple::ARMSubArch_v7;
bool IsThumbMode = IsThumbArch ||
Args.hasFlag(options::OPT_mthumb, options::OPT_mno_thumb, false) ||
(IsArmArch && llvm::ARM::parseArchISA(Arch) == llvm::ARM::ISAKind::THUMB);
bool IsArmV7Mode = (IsArmArch || IsThumbArch) &&
(llvm::ARM::parseArchVersion(Arch) == 7 ||
(IsArmArch && Arch == "" && IsV7SubArch));
addMultilibFlag(IsArmV7Mode, "-march=armv7-a", Flags);
addMultilibFlag(IsThumbMode, "-mthumb", Flags);
if (AndroidArmMultilibs.select(D, Flags, Result.SelectedMultilibs))
Result.Multilibs = AndroidArmMultilibs;
}
static bool findMSP430Multilibs(const Driver &D,
const llvm::Triple &TargetTriple,
StringRef Path, const ArgList &Args,
DetectedMultilibs &Result) {
FilterNonExistent NonExistent(Path, "/crtbegin.o", D.getVFS());
MultilibBuilder WithoutExceptions =
MultilibBuilder("/430").flag("-exceptions", /*Disallow=*/true);
MultilibBuilder WithExceptions =
MultilibBuilder("/430/exceptions").flag("-exceptions");
// FIXME: when clang starts to support msp430x ISA additional logic
// to select between multilib must be implemented
// MultilibBuilder MSP430xMultilib = MultilibBuilder("/large");
Result.Multilibs.push_back(WithoutExceptions.makeMultilib());
Result.Multilibs.push_back(WithExceptions.makeMultilib());
Result.Multilibs.FilterOut(NonExistent);
Multilib::flags_list Flags;
addMultilibFlag(Args.hasFlag(options::OPT_fexceptions,
options::OPT_fno_exceptions, false),
"-exceptions", Flags);
if (Result.Multilibs.select(D, Flags, Result.SelectedMultilibs))
return true;
return false;
}
static void findCSKYMultilibs(const Driver &D, const llvm::Triple &TargetTriple,
StringRef Path, const ArgList &Args,
DetectedMultilibs &Result) {
FilterNonExistent NonExistent(Path, "/crtbegin.o", D.getVFS());
tools::csky::FloatABI TheFloatABI = tools::csky::getCSKYFloatABI(D, Args);
std::optional<llvm::StringRef> Res =
tools::csky::getCSKYArchName(D, Args, TargetTriple);
if (!Res)
return;
auto ARCHName = *Res;
Multilib::flags_list Flags;
addMultilibFlag(TheFloatABI == tools::csky::FloatABI::Hard, "-hard-fp",
Flags);
addMultilibFlag(TheFloatABI == tools::csky::FloatABI::SoftFP, "-soft-fp",
Flags);
addMultilibFlag(TheFloatABI == tools::csky::FloatABI::Soft, "-soft", Flags);
addMultilibFlag(ARCHName == "ck801", "-march=ck801", Flags);
addMultilibFlag(ARCHName == "ck802", "-march=ck802", Flags);
addMultilibFlag(ARCHName == "ck803", "-march=ck803", Flags);
addMultilibFlag(ARCHName == "ck804", "-march=ck804", Flags);
addMultilibFlag(ARCHName == "ck805", "-march=ck805", Flags);
addMultilibFlag(ARCHName == "ck807", "-march=ck807", Flags);
addMultilibFlag(ARCHName == "ck810", "-march=ck810", Flags);
addMultilibFlag(ARCHName == "ck810v", "-march=ck810v", Flags);
addMultilibFlag(ARCHName == "ck860", "-march=ck860", Flags);
addMultilibFlag(ARCHName == "ck860v", "-march=ck860v", Flags);
bool isBigEndian = false;
if (Arg *A = Args.getLastArg(options::OPT_mlittle_endian,
options::OPT_mbig_endian))
isBigEndian = !A->getOption().matches(options::OPT_mlittle_endian);
addMultilibFlag(isBigEndian, "-EB", Flags);
auto HardFloat = MultilibBuilder("/hard-fp").flag("-hard-fp");
auto SoftFpFloat = MultilibBuilder("/soft-fp").flag("-soft-fp");
auto SoftFloat = MultilibBuilder("").flag("-soft");
auto Arch801 = MultilibBuilder("/ck801").flag("-march=ck801");
auto Arch802 = MultilibBuilder("/ck802").flag("-march=ck802");
auto Arch803 = MultilibBuilder("/ck803").flag("-march=ck803");
// CK804 use the same library as CK803
auto Arch804 = MultilibBuilder("/ck803").flag("-march=ck804");
auto Arch805 = MultilibBuilder("/ck805").flag("-march=ck805");
auto Arch807 = MultilibBuilder("/ck807").flag("-march=ck807");
auto Arch810 = MultilibBuilder("").flag("-march=ck810");
auto Arch810v = MultilibBuilder("/ck810v").flag("-march=ck810v");
auto Arch860 = MultilibBuilder("/ck860").flag("-march=ck860");
auto Arch860v = MultilibBuilder("/ck860v").flag("-march=ck860v");
auto BigEndian = MultilibBuilder("/big").flag("-EB");
MultilibSet CSKYMultilibs =
MultilibSetBuilder()
.Maybe(BigEndian)
.Either({Arch801, Arch802, Arch803, Arch804, Arch805, Arch807,
Arch810, Arch810v, Arch860, Arch860v})
.Either(HardFloat, SoftFpFloat, SoftFloat)
.makeMultilibSet()
.FilterOut(NonExistent);
if (CSKYMultilibs.select(D, Flags, Result.SelectedMultilibs))
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 Driver &D, 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(D, 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);
// Ignore any error here, we assume it will be handled in another place.
if (llvm::errorToBool(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.starts_with("!march=") || Flag.starts_with("-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);
// Ignore any error here, we assume it will handled in another place.
if (llvm::errorToBool(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;
for (auto &MLConfigArchExt : MLConfigISAInfo->getExtensions()) {
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", /*Disallow*/ true);
} else {
NewMultilib.flag("-m32", /*Disallow*/ true);
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", /*Disallow*/ true);
}
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(D, 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,
DetectedMultilibs &Result) {
FilterNonExistent NonExistent(Path, "/crtbegin.o", D.getVFS());
struct RiscvMultilib {
StringRef march;
StringRef mabi;
};
// 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"}};
std::vector<MultilibBuilder> Ms;
for (auto Element : RISCVMultilibSet) {
// multilib path rule is ${march}/${mabi}
Ms.emplace_back(
MultilibBuilder(
(Twine(Element.march) + "/" + Twine(Element.mabi)).str())
.flag(Twine("-march=", Element.march).str())
.flag(Twine("-mabi=", Element.mabi).str()));
}
MultilibSet RISCVMultilibs =
MultilibSetBuilder()
.Either(Ms)
.makeMultilibSet()
.FilterOut(NonExistent)
.setFilePathsCallback([](const Multilib &M) {
return std::vector<std::string>(
{M.gccSuffix(),
"/../../../../riscv64-unknown-elf/lib" + M.gccSuffix(),
"/../../../../riscv32-unknown-elf/lib" + M.gccSuffix()});
});
Multilib::flags_list Flags;
llvm::StringSet<> Added_ABIs;
StringRef ABIName = tools::riscv::getRISCVABI(Args, TargetTriple);
std::string MArch = tools::riscv::getRISCVArch(Args, TargetTriple);
for (auto Element : RISCVMultilibSet) {
addMultilibFlag(MArch == Element.march,
Twine("-march=", Element.march).str().c_str(), Flags);
if (!Added_ABIs.count(Element.mabi)) {
Added_ABIs.insert(Element.mabi);
addMultilibFlag(ABIName == Element.mabi,
Twine("-mabi=", Element.mabi).str().c_str(), Flags);
}
}
if (selectRISCVMultilib(D, RISCVMultilibs, MArch, Flags,
Result.SelectedMultilibs))
Result.Multilibs = RISCVMultilibs;
}
static void findRISCVMultilibs(const Driver &D,
const llvm::Triple &TargetTriple, StringRef Path,
const ArgList &Args, DetectedMultilibs &Result) {
if (TargetTriple.getOS() == llvm::Triple::UnknownOS)
return findRISCVBareMetalMultilibs(D, TargetTriple, Path, Args, Result);
FilterNonExistent NonExistent(Path, "/crtbegin.o", D.getVFS());
MultilibBuilder Ilp32 =
MultilibBuilder("lib32/ilp32").flag("-m32").flag("-mabi=ilp32");
MultilibBuilder Ilp32f =
MultilibBuilder("lib32/ilp32f").flag("-m32").flag("-mabi=ilp32f");
MultilibBuilder Ilp32d =
MultilibBuilder("lib32/ilp32d").flag("-m32").flag("-mabi=ilp32d");
MultilibBuilder Lp64 =
MultilibBuilder("lib64/lp64").flag("-m64").flag("-mabi=lp64");
MultilibBuilder Lp64f =
MultilibBuilder("lib64/lp64f").flag("-m64").flag("-mabi=lp64f");
MultilibBuilder Lp64d =
MultilibBuilder("lib64/lp64d").flag("-m64").flag("-mabi=lp64d");
MultilibSet RISCVMultilibs =
MultilibSetBuilder()
.Either({Ilp32, Ilp32f, Ilp32d, Lp64, Lp64f, Lp64d})
.makeMultilibSet()
.FilterOut(NonExistent);
Multilib::flags_list Flags;
bool IsRV64 = TargetTriple.getArch() == llvm::Triple::riscv64;
StringRef ABIName = tools::riscv::getRISCVABI(Args, TargetTriple);
addMultilibFlag(!IsRV64, "-m32", Flags);
addMultilibFlag(IsRV64, "-m64", Flags);
addMultilibFlag(ABIName == "ilp32", "-mabi=ilp32", Flags);
addMultilibFlag(ABIName == "ilp32f", "-mabi=ilp32f", Flags);
addMultilibFlag(ABIName == "ilp32d", "-mabi=ilp32d", Flags);
addMultilibFlag(ABIName == "lp64", "-mabi=lp64", Flags);
addMultilibFlag(ABIName == "lp64f", "-mabi=lp64f", Flags);
addMultilibFlag(ABIName == "lp64d", "-mabi=lp64d", Flags);
if (RISCVMultilibs.select(D, Flags, Result.SelectedMultilibs))
Result.Multilibs = RISCVMultilibs;
}
static bool findBiarchMultilibs(const Driver &D,
const llvm::Triple &TargetTriple,
StringRef Path, const ArgList &Args,
bool NeedsBiarchSuffix,
DetectedMultilibs &Result) {
MultilibBuilder DefaultBuilder;
// Some versions of SUSE and Fedora on ppc64 put 32-bit libs
// in what would normally be GCCInstallPath and put the 64-bit
// libs in a subdirectory named 64. The simple logic we follow is that
// *if* there is a subdirectory of the right name with crtbegin.o in it,
// we use that. If not, and if not a biarch triple alias, we look for
// crtbegin.o without the subdirectory.
StringRef Suff64 = "/64";
// Solaris uses platform-specific suffixes instead of /64.
if (TargetTriple.isOSSolaris()) {
switch (TargetTriple.getArch()) {
case llvm::Triple::x86:
case llvm::Triple::x86_64:
Suff64 = "/amd64";
break;
case llvm::Triple::sparc:
case llvm::Triple::sparcv9:
Suff64 = "/sparcv9";
break;
default:
break;
}
}
Multilib Alt64 = MultilibBuilder()
.gccSuffix(Suff64)
.includeSuffix(Suff64)
.flag("-m32", /*Disallow=*/true)
.flag("-m64")
.flag("-mx32", /*Disallow=*/true)
.makeMultilib();
Multilib Alt32 = MultilibBuilder()
.gccSuffix("/32")
.includeSuffix("/32")
.flag("-m32")
.flag("-m64", /*Disallow=*/true)
.flag("-mx32", /*Disallow=*/true)
.makeMultilib();
Multilib Altx32 = MultilibBuilder()
.gccSuffix("/x32")
.includeSuffix("/x32")
.flag("-m32", /*Disallow=*/true)
.flag("-m64", /*Disallow=*/true)
.flag("-mx32")
.makeMultilib();
Multilib Alt32sparc = MultilibBuilder()
.gccSuffix("/sparcv8plus")
.includeSuffix("/sparcv8plus")
.flag("-m32")
.flag("-m64", /*Disallow=*/true)
.makeMultilib();
// GCC toolchain for IAMCU doesn't have crtbegin.o, so look for libgcc.a.
FilterNonExistent NonExistent(
Path, TargetTriple.isOSIAMCU() ? "/libgcc.a" : "/crtbegin.o", D.getVFS());
// Determine default multilib from: 32, 64, x32
// Also handle cases such as 64 on 32, 32 on 64, etc.
enum { UNKNOWN, WANT32, WANT64, WANTX32 } Want = UNKNOWN;
const bool IsX32 = TargetTriple.isX32();
if (TargetTriple.isArch32Bit() && !NonExistent(Alt32))
Want = WANT64;
if (TargetTriple.isArch32Bit() && !NonExistent(Alt32sparc))
Want = WANT64;
else if (TargetTriple.isArch64Bit() && IsX32 && !NonExistent(Altx32))
Want = WANT64;
else if (TargetTriple.isArch64Bit() && !IsX32 && !NonExistent(Alt64))
Want = WANT32;
else if (TargetTriple.isArch64Bit() && !NonExistent(Alt32sparc))
Want = WANT64;
else {
if (TargetTriple.isArch32Bit())
Want = NeedsBiarchSuffix ? WANT64 : WANT32;
else if (IsX32)
Want = NeedsBiarchSuffix ? WANT64 : WANTX32;
else
Want = NeedsBiarchSuffix ? WANT32 : WANT64;
}
if (Want == WANT32)
DefaultBuilder.flag("-m32")
.flag("-m64", /*Disallow=*/true)
.flag("-mx32", /*Disallow=*/true);
else if (Want == WANT64)
DefaultBuilder.flag("-m32", /*Disallow=*/true)
.flag("-m64")
.flag("-mx32", /*Disallow=*/true);
else if (Want == WANTX32)
DefaultBuilder.flag("-m32", /*Disallow=*/true)
.flag("-m64", /*Disallow=*/true)
.flag("-mx32");
else
return false;
Multilib Default = DefaultBuilder.makeMultilib();
Result.Multilibs.push_back(Default);
Result.Multilibs.push_back(Alt64);
Result.Multilibs.push_back(Alt32);
Result.Multilibs.push_back(Altx32);
Result.Multilibs.push_back(Alt32sparc);
Result.Multilibs.FilterOut(NonExistent);
Multilib::flags_list Flags;
addMultilibFlag(TargetTriple.isArch64Bit() && !IsX32, "-m64", Flags);
addMultilibFlag(TargetTriple.isArch32Bit(), "-m32", Flags);
addMultilibFlag(TargetTriple.isArch64Bit() && IsX32, "-mx32", Flags);
if (!Result.Multilibs.select(D, Flags, Result.SelectedMultilibs))
return false;
if (Result.SelectedMultilibs.back() == Alt64 ||
Result.SelectedMultilibs.back() == Alt32 ||
Result.SelectedMultilibs.back() == Altx32 ||
Result.SelectedMultilibs.back() == Alt32sparc)
Result.BiarchSibling = Default;
return true;
}
/// Generic_GCC - A tool chain using the 'gcc' command to perform
/// all subcommands; this relies on gcc translating the majority of
/// command line options.
/// Less-than for GCCVersion, implementing a Strict Weak Ordering.
bool Generic_GCC::GCCVersion::isOlderThan(int RHSMajor, int RHSMinor,
int RHSPatch,
StringRef RHSPatchSuffix) const {
if (Major != RHSMajor)
return Major < RHSMajor;
if (Minor != RHSMinor) {
// Note that versions without a specified minor sort higher than those with
// a minor.
if (RHSMinor == -1)
return true;
if (Minor == -1)
return false;
return Minor < RHSMinor;
}
if (Patch != RHSPatch) {
// Note that versions without a specified patch sort higher than those with
// a patch.
if (RHSPatch == -1)
return true;
if (Patch == -1)
return false;
// Otherwise just sort on the patch itself.
return Patch < RHSPatch;
}
if (PatchSuffix != RHSPatchSuffix) {
// Sort empty suffixes higher.
if (RHSPatchSuffix.empty())
return true;
if (PatchSuffix.empty())
return false;
// Provide a lexicographic sort to make this a total ordering.
return PatchSuffix < RHSPatchSuffix;
}
// The versions are equal.
return false;
}
/// Parse a GCCVersion object out of a string of text.
///
/// This is the primary means of forming GCCVersion objects.
/*static*/
Generic_GCC::GCCVersion Generic_GCC::GCCVersion::Parse(StringRef VersionText) {
const GCCVersion BadVersion = {VersionText.str(), -1, -1, -1, "", "", ""};
std::pair<StringRef, StringRef> First = VersionText.split('.');
std::pair<StringRef, StringRef> Second = First.second.split('.');
StringRef MajorStr = First.first;
StringRef MinorStr = Second.first;
StringRef PatchStr = Second.second;
GCCVersion GoodVersion = {VersionText.str(), -1, -1, -1, "", "", ""};
// Parse version number strings such as:
// 5
// 4.4
// 4.4-patched
// 4.4.0
// 4.4.x
// 4.4.2-rc4
// 4.4.x-patched
// 10-win32
// Split on '.', handle 1, 2 or 3 such segments. Each segment must contain
// purely a number, except for the last one, where a non-number suffix
// is stored in PatchSuffix. The third segment is allowed to not contain
// a number at all.
auto TryParseLastNumber = [&](StringRef Segment, int &Number,
std::string &OutStr) -> bool {
// Look for a number prefix and parse that, and split out any trailing
// string into GoodVersion.PatchSuffix.
if (size_t EndNumber = Segment.find_first_not_of("0123456789")) {
StringRef NumberStr = Segment.slice(0, EndNumber);
if (NumberStr.getAsInteger(10, Number) || Number < 0)
return false;
OutStr = NumberStr;
GoodVersion.PatchSuffix = Segment.substr(EndNumber);
return true;
}
return false;
};
auto TryParseNumber = [](StringRef Segment, int &Number) -> bool {
if (Segment.getAsInteger(10, Number) || Number < 0)
return false;
return true;
};
if (MinorStr.empty()) {
// If no minor string, major is the last segment
if (!TryParseLastNumber(MajorStr, GoodVersion.Major, GoodVersion.MajorStr))
return BadVersion;
return GoodVersion;
}
if (!TryParseNumber(MajorStr, GoodVersion.Major))
return BadVersion;
GoodVersion.MajorStr = MajorStr;
if (PatchStr.empty()) {
// If no patch string, minor is the last segment
if (!TryParseLastNumber(MinorStr, GoodVersion.Minor, GoodVersion.MinorStr))
return BadVersion;
return GoodVersion;
}
if (!TryParseNumber(MinorStr, GoodVersion.Minor))
return BadVersion;
GoodVersion.MinorStr = MinorStr;
// For the last segment, tolerate a missing number.
std::string DummyStr;
TryParseLastNumber(PatchStr, GoodVersion.Patch, DummyStr);
return GoodVersion;
}
static llvm::StringRef getGCCToolchainDir(const ArgList &Args,
llvm::StringRef SysRoot) {
const Arg *A = Args.getLastArg(clang::driver::options::OPT_gcc_toolchain);
if (A)
return A->getValue();
// If we have a SysRoot, ignore GCC_INSTALL_PREFIX.
// GCC_INSTALL_PREFIX specifies the gcc installation for the default
// sysroot and is likely not valid with a different sysroot.
if (!SysRoot.empty())
return "";
return GCC_INSTALL_PREFIX;
}
/// Initialize a GCCInstallationDetector from the driver.
///
/// This performs all of the autodetection and sets up the various paths.
/// Once constructed, a GCCInstallationDetector is essentially immutable.
///
/// FIXME: We shouldn't need an explicit TargetTriple parameter here, and
/// should instead pull the target out of the driver. This is currently
/// necessary because the driver doesn't store the final version of the target
/// triple.
void Generic_GCC::GCCInstallationDetector::init(
const llvm::Triple &TargetTriple, const ArgList &Args) {
llvm::Triple BiarchVariantTriple = TargetTriple.isArch32Bit()
? TargetTriple.get64BitArchVariant()
: TargetTriple.get32BitArchVariant();
// The library directories which may contain GCC installations.
SmallVector<StringRef, 4> CandidateLibDirs, CandidateBiarchLibDirs;
// The compatible GCC triples for this particular architecture.
SmallVector<StringRef, 16> CandidateTripleAliases;
SmallVector<StringRef, 16> CandidateBiarchTripleAliases;
// Add some triples that we want to check first.
CandidateTripleAliases.push_back(TargetTriple.str());
std::string TripleNoVendor, BiarchTripleNoVendor;
if (TargetTriple.getVendor() == llvm::Triple::UnknownVendor) {
StringRef OSEnv = TargetTriple.getOSAndEnvironmentName();
if (TargetTriple.getEnvironment() == llvm::Triple::GNUX32)
OSEnv = "linux-gnu";
TripleNoVendor = (TargetTriple.getArchName().str() + '-' + OSEnv).str();
CandidateTripleAliases.push_back(TripleNoVendor);
if (BiarchVariantTriple.getArch() != llvm::Triple::UnknownArch) {
BiarchTripleNoVendor =
(BiarchVariantTriple.getArchName().str() + '-' + OSEnv).str();
CandidateBiarchTripleAliases.push_back(BiarchTripleNoVendor);
}
}
CollectLibDirsAndTriples(TargetTriple, BiarchVariantTriple, CandidateLibDirs,
CandidateTripleAliases, CandidateBiarchLibDirs,
CandidateBiarchTripleAliases);
// If --gcc-install-dir= is specified, skip filesystem detection.
if (const Arg *A =
Args.getLastArg(clang::driver::options::OPT_gcc_install_dir_EQ);
A && A->getValue()[0]) {
StringRef InstallDir = A->getValue();
if (!ScanGCCForMultilibs(TargetTriple, Args, InstallDir, false)) {
D.Diag(diag::err_drv_invalid_gcc_install_dir) << InstallDir;
} else {
(void)InstallDir.consume_back("/");
StringRef VersionText = llvm::sys::path::filename(InstallDir);
StringRef TripleText =
llvm::sys::path::filename(llvm::sys::path::parent_path(InstallDir));
SelectedInstallation.Version = GCCVersion::Parse(VersionText);
SelectedInstallation.GCCTriple.setTriple(TripleText);
SelectedInstallation.GCCInstallPath = std::string(InstallDir);
SelectedInstallation.GCCParentLibPath =
SelectedInstallation.GCCInstallPath + "/../../..";
IsValid = true;
}
return;
}
// If --gcc-triple is specified use this instead of trying to
// auto-detect a triple.
if (const Arg *A =
Args.getLastArg(clang::driver::options::OPT_gcc_triple_EQ)) {
StringRef GCCTriple = A->getValue();
CandidateTripleAliases.clear();
CandidateTripleAliases.push_back(GCCTriple);
}
// Compute the set of prefixes for our search.
SmallVector<std::string, 8> Prefixes;
StringRef GCCToolchainDir = getGCCToolchainDir(Args, D.SysRoot);
if (GCCToolchainDir != "") {
if (GCCToolchainDir.back() == '/')
GCCToolchainDir = GCCToolchainDir.drop_back(); // remove the /
Prefixes.push_back(std::string(GCCToolchainDir));
} else {
// If we have a SysRoot, try that first.
if (!D.SysRoot.empty()) {
Prefixes.push_back(D.SysRoot);
AddDefaultGCCPrefixes(TargetTriple, Prefixes, D.SysRoot);
}
// Then look for gcc installed alongside clang.
Prefixes.push_back(D.Dir + "/..");
// Next, look for prefix(es) that correspond to distribution-supplied gcc
// installations.
if (D.SysRoot.empty()) {
// Typically /usr.
AddDefaultGCCPrefixes(TargetTriple, Prefixes, D.SysRoot);
}
// Try to respect gcc-config on Gentoo if --gcc-toolchain is not provided.
// This avoids accidentally enforcing the system GCC version when using a
// custom toolchain.
SmallVector<StringRef, 16> GentooTestTriples;
// Try to match an exact triple as target triple first.
// e.g. crossdev -S x86_64-gentoo-linux-gnu will install gcc libs for
// x86_64-gentoo-linux-gnu. But "clang -target x86_64-gentoo-linux-gnu"
// may pick the libraries for x86_64-pc-linux-gnu even when exact matching
// triple x86_64-gentoo-linux-gnu is present.
GentooTestTriples.push_back(TargetTriple.str());
GentooTestTriples.append(CandidateTripleAliases.begin(),
CandidateTripleAliases.end());
if (ScanGentooConfigs(TargetTriple, Args, GentooTestTriples,
CandidateBiarchTripleAliases))
return;
}
// Loop over the various components which exist and select the best GCC
// installation available. GCC installs are ranked by version number.
const GCCVersion VersionZero = GCCVersion::Parse("0.0.0");
SelectedInstallation.Version = VersionZero;
for (const std::string &Prefix : Prefixes) {
auto &VFS = D.getVFS();
if (!VFS.exists(Prefix))
continue;
for (StringRef Suffix : CandidateLibDirs) {
const std::string LibDir = concat(Prefix, Suffix);
if (!VFS.exists(LibDir))
continue;
// Maybe filter out <libdir>/gcc and <libdir>/gcc-cross.
bool GCCDirExists = VFS.exists(LibDir + "/gcc");
bool GCCCrossDirExists = VFS.exists(LibDir + "/gcc-cross");
for (StringRef Candidate : CandidateTripleAliases)
ScanLibDirForGCCTriple(TargetTriple, Args, LibDir, Candidate, false,
GCCDirExists, GCCCrossDirExists);
}
for (StringRef Suffix : CandidateBiarchLibDirs) {
const std::string LibDir = Prefix + Suffix.str();
if (!VFS.exists(LibDir))
continue;
bool GCCDirExists = VFS.exists(LibDir + "/gcc");
bool GCCCrossDirExists = VFS.exists(LibDir + "/gcc-cross");
for (StringRef Candidate : CandidateBiarchTripleAliases)
ScanLibDirForGCCTriple(TargetTriple, Args, LibDir, Candidate, true,
GCCDirExists, GCCCrossDirExists);
}
// Skip other prefixes once a GCC installation is found.
if (SelectedInstallation.Version > VersionZero)
break;
}
}
void Generic_GCC::GCCInstallationDetector::print(raw_ostream &OS) const {
for (const auto &InstallPath : CandidateGCCInstallPaths)
OS << "Found candidate GCC installation: " << InstallPath << "\n";
if (!SelectedInstallation.GCCInstallPath.empty())
OS << "Selected GCC installation: " << SelectedInstallation.GCCInstallPath
<< "\n";
for (const auto &Multilib : Multilibs)
OS << "Candidate multilib: " << Multilib << "\n";
if (Multilibs.size() != 0 ||
!SelectedInstallation.SelectedMultilib.isDefault())
OS << "Selected multilib: " << SelectedInstallation.SelectedMultilib
<< "\n";
}
bool Generic_GCC::GCCInstallationDetector::getBiarchSibling(Multilib &M) const {
if (BiarchSibling) {
M = *BiarchSibling;
return true;
}
return false;
}
void Generic_GCC::GCCInstallationDetector::AddDefaultGCCPrefixes(
const llvm::Triple &TargetTriple, SmallVectorImpl<std::string> &Prefixes,
StringRef SysRoot) {
if (TargetTriple.isOSHaiku()) {
Prefixes.push_back(concat(SysRoot, "/boot/system/develop/tools"));
return;
}
if (TargetTriple.isOSSolaris()) {
// Solaris is a special case.
// The GCC installation is under
// /usr/gcc/<major>.<minor>/lib/gcc/<triple>/<major>.<minor>.<patch>/
// so we need to find those /usr/gcc/*/lib/gcc libdirs and go with
// /usr/gcc/<version> as a prefix.
SmallVector<std::pair<GCCVersion, std::string>, 8> SolarisPrefixes;
std::string PrefixDir = concat(SysRoot, "/usr/gcc");
std::error_code EC;
for (llvm::vfs::directory_iterator LI = D.getVFS().dir_begin(PrefixDir, EC),
LE;
!EC && LI != LE; LI = LI.increment(EC)) {
StringRef VersionText = llvm::sys::path::filename(LI->path());
GCCVersion CandidateVersion = GCCVersion::Parse(VersionText);
// Filter out obviously bad entries.
if (CandidateVersion.Major == -1 || CandidateVersion.isOlderThan(4, 1, 1))
continue;
std::string CandidatePrefix = PrefixDir + "/" + VersionText.str();
std::string CandidateLibPath = CandidatePrefix + "/lib/gcc";
if (!D.getVFS().exists(CandidateLibPath))
continue;
SolarisPrefixes.emplace_back(
std::make_pair(CandidateVersion, CandidatePrefix));
}
// Sort in reverse order so GCCInstallationDetector::init picks the latest.
std::sort(SolarisPrefixes.rbegin(), SolarisPrefixes.rend());
for (auto p : SolarisPrefixes)
Prefixes.emplace_back(p.second);
return;
}
// For Linux, if --sysroot is not specified, look for RHEL/CentOS devtoolsets
// and gcc-toolsets.
if (SysRoot.empty() && TargetTriple.getOS() == llvm::Triple::Linux &&
D.getVFS().exists("/opt/rh")) {
// TODO: We may want to remove this, since the functionality
// can be achieved using config files.
Prefixes.push_back("/opt/rh/gcc-toolset-12/root/usr");
Prefixes.push_back("/opt/rh/gcc-toolset-11/root/usr");
Prefixes.push_back("/opt/rh/gcc-toolset-10/root/usr");
Prefixes.push_back("/opt/rh/devtoolset-12/root/usr");
Prefixes.push_back("/opt/rh/devtoolset-11/root/usr");
Prefixes.push_back("/opt/rh/devtoolset-10/root/usr");
Prefixes.push_back("/opt/rh/devtoolset-9/root/usr");
Prefixes.push_back("/opt/rh/devtoolset-8/root/usr");
Prefixes.push_back("/opt/rh/devtoolset-7/root/usr");
Prefixes.push_back("/opt/rh/devtoolset-6/root/usr");
Prefixes.push_back("/opt/rh/devtoolset-4/root/usr");
Prefixes.push_back("/opt/rh/devtoolset-3/root/usr");
Prefixes.push_back("/opt/rh/devtoolset-2/root/usr");
}
// Fall back to /usr which is used by most non-Solaris systems.
Prefixes.push_back(concat(SysRoot, "/usr"));
}
/*static*/ void Generic_GCC::GCCInstallationDetector::CollectLibDirsAndTriples(
const llvm::Triple &TargetTriple, const llvm::Triple &BiarchTriple,
SmallVectorImpl<StringRef> &LibDirs,
SmallVectorImpl<StringRef> &TripleAliases,
SmallVectorImpl<StringRef> &BiarchLibDirs,
SmallVectorImpl<StringRef> &BiarchTripleAliases) {
// Declare a bunch of static data sets that we'll select between below. These
// are specifically designed to always refer to string literals to avoid any
// lifetime or initialization issues.
//
// The *Triples variables hard code some triples so that, for example,
// --target=aarch64 (incomplete triple) can detect lib/aarch64-linux-gnu.
// They are not needed when the user has correct LLVM_DEFAULT_TARGET_TRIPLE
// and always uses the full --target (e.g. --target=aarch64-linux-gnu). The
// lists should shrink over time. Please don't add more elements to *Triples.
static const char *const AArch64LibDirs[] = {"/lib64", "/lib"};
static const char *const AArch64Triples[] = {
"aarch64-none-linux-gnu", "aarch64-redhat-linux", "aarch64-suse-linux"};
static const char *const AArch64beLibDirs[] = {"/lib"};
static const char *const AArch64beTriples[] = {"aarch64_be-none-linux-gnu"};
static const char *const ARMLibDirs[] = {"/lib"};
static const char *const ARMTriples[] = {"arm-linux-gnueabi"};
static const char *const ARMHFTriples[] = {"arm-linux-gnueabihf",
"armv7hl-redhat-linux-gnueabi",
"armv6hl-suse-linux-gnueabi",
"armv7hl-suse-linux-gnueabi"};
static const char *const ARMebLibDirs[] = {"/lib"};
static const char *const ARMebTriples[] = {"armeb-linux-gnueabi"};
static const char *const ARMebHFTriples[] = {
"armeb-linux-gnueabihf", "armebv7hl-redhat-linux-gnueabi"};
static const char *const AVRLibDirs[] = {"/lib"};
static const char *const AVRTriples[] = {"avr"};
static const char *const CSKYLibDirs[] = {"/lib"};
static const char *const CSKYTriples[] = {
"csky-linux-gnuabiv2", "csky-linux-uclibcabiv2", "csky-elf-noneabiv2"};
static const char *const X86_64LibDirs[] = {"/lib64", "/lib"};
static const char *const X86_64Triples[] = {
"x86_64-linux-gnu", "x86_64-unknown-linux-gnu",
"x86_64-pc-linux-gnu", "x86_64-redhat-linux6E",
"x86_64-redhat-linux", "x86_64-suse-linux",
"x86_64-manbo-linux-gnu", "x86_64-slackware-linux",
"x86_64-unknown-linux", "x86_64-amazon-linux"};
static const char *const X32Triples[] = {"x86_64-linux-gnux32",
"x86_64-pc-linux-gnux32"};
static const char *const X32LibDirs[] = {"/libx32", "/lib"};
static const char *const X86LibDirs[] = {"/lib32", "/lib"};
static const char *const X86Triples[] = {
"i586-linux-gnu", "i686-linux-gnu", "i686-pc-linux-gnu",
"i386-redhat-linux6E", "i686-redhat-linux", "i386-redhat-linux",
"i586-suse-linux", "i686-montavista-linux",
};
static const char *const LoongArch64LibDirs[] = {"/lib64", "/lib"};
static const char *const LoongArch64Triples[] = {
"loongarch64-linux-gnu", "loongarch64-unknown-linux-gnu"};
static const char *const M68kLibDirs[] = {"/lib"};
static const char *const M68kTriples[] = {"m68k-unknown-linux-gnu",
"m68k-suse-linux"};
static const char *const MIPSLibDirs[] = {"/libo32", "/lib"};
static const char *const MIPSTriples[] = {
"mips-linux-gnu", "mips-mti-linux", "mips-mti-linux-gnu",
"mips-img-linux-gnu", "mipsisa32r6-linux-gnu"};
static const char *const MIPSELLibDirs[] = {"/libo32", "/lib"};
static const char *const MIPSELTriples[] = {"mipsel-linux-gnu",
"mips-img-linux-gnu"};
static const char *const MIPS64LibDirs[] = {"/lib64", "/lib"};
static const char *const MIPS64Triples[] = {
"mips-mti-linux-gnu", "mips-img-linux-gnu", "mips64-linux-gnuabi64",
"mipsisa64r6-linux-gnu", "mipsisa64r6-linux-gnuabi64"};
static const char *const MIPS64ELLibDirs[] = {"/lib64", "/lib"};
static const char *const MIPS64ELTriples[] = {
"mips-mti-linux-gnu", "mips-img-linux-gnu", "mips64el-linux-gnuabi64",
"mipsisa64r6el-linux-gnu", "mipsisa64r6el-linux-gnuabi64"};
static const char *const MIPSN32LibDirs[] = {"/lib32"};
static const char *const MIPSN32Triples[] = {"mips64-linux-gnuabin32",
"mipsisa64r6-linux-gnuabin32"};
static const char *const MIPSN32ELLibDirs[] = {"/lib32"};
static const char *const MIPSN32ELTriples[] = {
"mips64el-linux-gnuabin32", "mipsisa64r6el-linux-gnuabin32"};
static const char *const MSP430LibDirs[] = {"/lib"};
static const char *const MSP430Triples[] = {"msp430-elf"};
static const char *const PPCLibDirs[] = {"/lib32", "/lib"};
static const char *const PPCTriples[] = {
"powerpc-unknown-linux-gnu",
// On 32-bit PowerPC systems running SUSE Linux, gcc is configured as a
// 64-bit compiler which defaults to "-m32", hence "powerpc64-suse-linux".
"powerpc64-suse-linux", "powerpc-montavista-linuxspe"};
static const char *const PPCLELibDirs[] = {"/lib32", "/lib"};
static const char *const PPCLETriples[] = {"powerpcle-unknown-linux-gnu",
"powerpcle-linux-musl"};
static const char *const PPC64LibDirs[] = {"/lib64", "/lib"};
static const char *const PPC64Triples[] = {"powerpc64-unknown-linux-gnu",
"powerpc64-suse-linux",
"ppc64-redhat-linux"};
static const char *const PPC64LELibDirs[] = {"/lib64", "/lib"};
static const char *const PPC64LETriples[] = {
"powerpc64le-unknown-linux-gnu", "powerpc64le-none-linux-gnu",
"powerpc64le-suse-linux", "ppc64le-redhat-linux"};
static const char *const RISCV32LibDirs[] = {"/lib32", "/lib"};
static const char *const RISCV32Triples[] = {"riscv32-unknown-linux-gnu",
"riscv32-unknown-elf"};
static const char *const RISCV64LibDirs[] = {"/lib64", "/lib"};
static const char *const RISCV64Triples[] = {"riscv64-unknown-linux-gnu",
"riscv64-unknown-elf"};
static const char *const SPARCv8LibDirs[] = {"/lib32", "/lib"};
static const char *const SPARCv8Triples[] = {"sparc-linux-gnu",
"sparcv8-linux-gnu"};
static const char *const SPARCv9LibDirs[] = {"/lib64", "/lib"};
static const char *const SPARCv9Triples[] = {"sparc64-linux-gnu",
"sparcv9-linux-gnu"};
static const char *const SystemZLibDirs[] = {"/lib64", "/lib"};
static const char *const SystemZTriples[] = {
"s390x-unknown-linux-gnu", "s390x-ibm-linux-gnu", "s390x-suse-linux",
"s390x-redhat-linux"};
using std::begin;
using std::end;
if (TargetTriple.isOSSolaris()) {
static const char *const SolarisLibDirs[] = {"/lib"};
static const char *const SolarisSparcV8Triples[] = {
"sparc-sun-solaris2.11"};
static const char *const SolarisSparcV9Triples[] = {
"sparcv9-sun-solaris2.11"};
static const char *const SolarisX86Triples[] = {"i386-pc-solaris2.11"};
static const char *const SolarisX86_64Triples[] = {"x86_64-pc-solaris2.11"};
LibDirs.append(begin(SolarisLibDirs), end(SolarisLibDirs));
BiarchLibDirs.append(begin(SolarisLibDirs), end(SolarisLibDirs));
switch (TargetTriple.getArch()) {
case llvm::Triple::x86:
TripleAliases.append(begin(SolarisX86Triples), end(SolarisX86Triples));
BiarchTripleAliases.append(begin(SolarisX86_64Triples),
end(SolarisX86_64Triples));
break;
case llvm::Triple::x86_64:
TripleAliases.append(begin(SolarisX86_64Triples),
end(SolarisX86_64Triples));
BiarchTripleAliases.append(begin(SolarisX86Triples),
end(SolarisX86Triples));
break;
case llvm::Triple::sparc:
TripleAliases.append(begin(SolarisSparcV8Triples),
end(SolarisSparcV8Triples));
BiarchTripleAliases.append(begin(SolarisSparcV9Triples),
end(SolarisSparcV9Triples));
break;
case llvm::Triple::sparcv9:
TripleAliases.append(begin(SolarisSparcV9Triples),
end(SolarisSparcV9Triples));
BiarchTripleAliases.append(begin(SolarisSparcV8Triples),
end(SolarisSparcV8Triples));
break;
default:
break;
}
return;
}
// Android targets should not use GNU/Linux tools or libraries.
if (TargetTriple.isAndroid()) {
static const char *const AArch64AndroidTriples[] = {
"aarch64-linux-android"};
static const char *const ARMAndroidTriples[] = {"arm-linux-androideabi"};
static const char *const X86AndroidTriples[] = {"i686-linux-android"};
static const char *const X86_64AndroidTriples[] = {"x86_64-linux-android"};
switch (TargetTriple.getArch()) {
case llvm::Triple::aarch64:
LibDirs.append(begin(AArch64LibDirs), end(AArch64LibDirs));
TripleAliases.append(begin(AArch64AndroidTriples),
end(AArch64AndroidTriples));
break;
case llvm::Triple::arm:
case llvm::Triple::thumb:
LibDirs.append(begin(ARMLibDirs), end(ARMLibDirs));
TripleAliases.append(begin(ARMAndroidTriples), end(ARMAndroidTriples));
break;
case llvm::Triple::x86_64:
LibDirs.append(begin(X86_64LibDirs), end(X86_64LibDirs));
TripleAliases.append(begin(X86_64AndroidTriples),
end(X86_64AndroidTriples));
BiarchLibDirs.append(begin(X86LibDirs), end(X86LibDirs));
BiarchTripleAliases.append(begin(X86AndroidTriples),
end(X86AndroidTriples));
break;
case llvm::Triple::x86:
LibDirs.append(begin(X86LibDirs), end(X86LibDirs));
TripleAliases.append(begin(X86AndroidTriples), end(X86AndroidTriples));
BiarchLibDirs.append(begin(X86_64LibDirs), end(X86_64LibDirs));
BiarchTripleAliases.append(begin(X86_64AndroidTriples),
end(X86_64AndroidTriples));
break;
default:
break;
}
return;
}
if (TargetTriple.isOSHurd()) {
switch (TargetTriple.getArch()) {
case llvm::Triple::x86_64:
LibDirs.append(begin(X86_64LibDirs), end(X86_64LibDirs));
TripleAliases.push_back("x86_64-gnu");
break;
case llvm::Triple::x86:
LibDirs.append(begin(X86LibDirs), end(X86LibDirs));
TripleAliases.push_back("i686-gnu");
break;
default:
break;
}
return;
}
if (TargetTriple.isWindowsCygwinEnvironment()) {
LibDirs.push_back("/lib");
switch (TargetTriple.getArch()) {
case llvm::Triple::x86_64:
TripleAliases.append({"x86_64-pc-cygwin", "x86_64-pc-msys"});
break;
case llvm::Triple::x86:
TripleAliases.append({"i686-pc-cygwin", "i686-pc-msys"});
break;
default:
break;
}
return;
}
switch (TargetTriple.getArch()) {
case llvm::Triple::aarch64:
LibDirs.append(begin(AArch64LibDirs), end(AArch64LibDirs));
TripleAliases.append(begin(AArch64Triples), end(AArch64Triples));
BiarchLibDirs.append(begin(AArch64LibDirs), end(AArch64LibDirs));
BiarchTripleAliases.append(begin(AArch64Triples), end(AArch64Triples));
break;
case llvm::Triple::aarch64_be:
LibDirs.append(begin(AArch64beLibDirs), end(AArch64beLibDirs));
TripleAliases.append(begin(AArch64beTriples), end(AArch64beTriples));
BiarchLibDirs.append(begin(AArch64beLibDirs), end(AArch64beLibDirs));
BiarchTripleAliases.append(begin(AArch64beTriples), end(AArch64beTriples));
break;
case llvm::Triple::arm:
case llvm::Triple::thumb:
LibDirs.append(begin(ARMLibDirs), end(ARMLibDirs));
if (TargetTriple.getEnvironment() == llvm::Triple::GNUEABIHF ||
TargetTriple.getEnvironment() == llvm::Triple::GNUEABIHFT64 ||
TargetTriple.getEnvironment() == llvm::Triple::MuslEABIHF ||
TargetTriple.getEnvironment() == llvm::Triple::EABIHF) {
TripleAliases.append(begin(ARMHFTriples), end(ARMHFTriples));
} else {
TripleAliases.append(begin(ARMTriples), end(ARMTriples));
}
break;
case llvm::Triple::armeb:
case llvm::Triple::thumbeb:
LibDirs.append(begin(ARMebLibDirs), end(ARMebLibDirs));
if (TargetTriple.getEnvironment() == llvm::Triple::GNUEABIHF ||
TargetTriple.getEnvironment() == llvm::Triple::GNUEABIHFT64 ||
TargetTriple.getEnvironment() == llvm::Triple::MuslEABIHF ||
TargetTriple.getEnvironment() == llvm::Triple::EABIHF) {
TripleAliases.append(begin(ARMebHFTriples), end(ARMebHFTriples));
} else {
TripleAliases.append(begin(ARMebTriples), end(ARMebTriples));
}
break;
case llvm::Triple::avr:
LibDirs.append(begin(AVRLibDirs), end(AVRLibDirs));
TripleAliases.append(begin(AVRTriples), end(AVRTriples));
break;
case llvm::Triple::csky:
LibDirs.append(begin(CSKYLibDirs), end(CSKYLibDirs));
TripleAliases.append(begin(CSKYTriples), end(CSKYTriples));
break;
case llvm::Triple::x86_64:
if (TargetTriple.isX32()) {
LibDirs.append(begin(X32LibDirs), end(X32LibDirs));
TripleAliases.append(begin(X32Triples), end(X32Triples));
BiarchLibDirs.append(begin(X86_64LibDirs), end(X86_64LibDirs));
BiarchTripleAliases.append(begin(X86_64Triples), end(X86_64Triples));
} else {
LibDirs.append(begin(X86_64LibDirs), end(X86_64LibDirs));
TripleAliases.append(begin(X86_64Triples), end(X86_64Triples));
BiarchLibDirs.append(begin(X32LibDirs), end(X32LibDirs));
BiarchTripleAliases.append(begin(X32Triples), end(X32Triples));
}
BiarchLibDirs.append(begin(X86LibDirs), end(X86LibDirs));
BiarchTripleAliases.append(begin(X86Triples), end(X86Triples));
break;
case llvm::Triple::x86:
LibDirs.append(begin(X86LibDirs), end(X86LibDirs));
// MCU toolchain is 32 bit only and its triple alias is TargetTriple
// itself, which will be appended below.
if (!TargetTriple.isOSIAMCU()) {
TripleAliases.append(begin(X86Triples), end(X86Triples));
BiarchLibDirs.append(begin(X86_64LibDirs), end(X86_64LibDirs));
BiarchTripleAliases.append(begin(X86_64Triples), end(X86_64Triples));
BiarchLibDirs.append(begin(X32LibDirs), end(X32LibDirs));
BiarchTripleAliases.append(begin(X32Triples), end(X32Triples));
}
break;
// TODO: Handle loongarch32.
case llvm::Triple::loongarch64:
LibDirs.append(begin(LoongArch64LibDirs), end(LoongArch64LibDirs));
TripleAliases.append(begin(LoongArch64Triples), end(LoongArch64Triples));
break;
case llvm::Triple::m68k:
LibDirs.append(begin(M68kLibDirs), end(M68kLibDirs));
TripleAliases.append(begin(M68kTriples), end(M68kTriples));
break;
case llvm::Triple::mips:
LibDirs.append(begin(MIPSLibDirs), end(MIPSLibDirs));
TripleAliases.append(begin(MIPSTriples), end(MIPSTriples));
BiarchLibDirs.append(begin(MIPS64LibDirs), end(MIPS64LibDirs));
BiarchTripleAliases.append(begin(MIPS64Triples), end(MIPS64Triples));
BiarchLibDirs.append(begin(MIPSN32LibDirs), end(MIPSN32LibDirs));
BiarchTripleAliases.append(begin(MIPSN32Triples), end(MIPSN32Triples));
break;
case llvm::Triple::mipsel:
LibDirs.append(begin(MIPSELLibDirs), end(MIPSELLibDirs));
TripleAliases.append(begin(MIPSELTriples), end(MIPSELTriples));
TripleAliases.append(begin(MIPSTriples), end(MIPSTriples));
BiarchLibDirs.append(begin(MIPS64ELLibDirs), end(MIPS64ELLibDirs));
BiarchTripleAliases.append(begin(MIPS64ELTriples), end(MIPS64ELTriples));
BiarchLibDirs.append(begin(MIPSN32ELLibDirs), end(MIPSN32ELLibDirs));
BiarchTripleAliases.append(begin(MIPSN32ELTriples), end(MIPSN32ELTriples));
break;
case llvm::Triple::mips64:
LibDirs.append(begin(MIPS64LibDirs), end(MIPS64LibDirs));
TripleAliases.append(begin(MIPS64Triples), end(MIPS64Triples));
BiarchLibDirs.append(begin(MIPSLibDirs), end(MIPSLibDirs));
BiarchTripleAliases.append(begin(MIPSTriples), end(MIPSTriples));
BiarchLibDirs.append(begin(MIPSN32LibDirs), end(MIPSN32LibDirs));
BiarchTripleAliases.append(begin(MIPSN32Triples), end(MIPSN32Triples));
break;
case llvm::Triple::mips64el:
LibDirs.append(begin(MIPS64ELLibDirs), end(MIPS64ELLibDirs));
TripleAliases.append(begin(MIPS64ELTriples), end(MIPS64ELTriples));
BiarchLibDirs.append(begin(MIPSELLibDirs), end(MIPSELLibDirs));
BiarchTripleAliases.append(begin(MIPSELTriples), end(MIPSELTriples));
BiarchLibDirs.append(begin(MIPSN32ELLibDirs), end(MIPSN32ELLibDirs));
BiarchTripleAliases.append(begin(MIPSN32ELTriples), end(MIPSN32ELTriples));
BiarchTripleAliases.append(begin(MIPSTriples), end(MIPSTriples));
break;
case llvm::Triple::msp430:
LibDirs.append(begin(MSP430LibDirs), end(MSP430LibDirs));
TripleAliases.append(begin(MSP430Triples), end(MSP430Triples));
break;
case llvm::Triple::ppc:
LibDirs.append(begin(PPCLibDirs), end(PPCLibDirs));
TripleAliases.append(begin(PPCTriples), end(PPCTriples));
BiarchLibDirs.append(begin(PPC64LibDirs), end(PPC64LibDirs));
BiarchTripleAliases.append(begin(PPC64Triples), end(PPC64Triples));
break;
case llvm::Triple::ppcle:
LibDirs.append(begin(PPCLELibDirs), end(PPCLELibDirs));
TripleAliases.append(begin(PPCLETriples), end(PPCLETriples));
BiarchLibDirs.append(begin(PPC64LELibDirs), end(PPC64LELibDirs));
BiarchTripleAliases.append(begin(PPC64LETriples), end(PPC64LETriples));
break;
case llvm::Triple::ppc64:
LibDirs.append(begin(PPC64LibDirs), end(PPC64LibDirs));
TripleAliases.append(begin(PPC64Triples), end(PPC64Triples));
BiarchLibDirs.append(begin(PPCLibDirs), end(PPCLibDirs));
BiarchTripleAliases.append(begin(PPCTriples), end(PPCTriples));
break;
case llvm::Triple::ppc64le:
LibDirs.append(begin(PPC64LELibDirs), end(PPC64LELibDirs));
TripleAliases.append(begin(PPC64LETriples), end(PPC64LETriples));
BiarchLibDirs.append(begin(PPCLELibDirs), end(PPCLELibDirs));
BiarchTripleAliases.append(begin(PPCLETriples), end(PPCLETriples));
break;
case llvm::Triple::riscv32:
LibDirs.append(begin(RISCV32LibDirs), end(RISCV32LibDirs));
TripleAliases.append(begin(RISCV32Triples), end(RISCV32Triples));
BiarchLibDirs.append(begin(RISCV64LibDirs), end(RISCV64LibDirs));
BiarchTripleAliases.append(begin(RISCV64Triples), end(RISCV64Triples));
break;
case llvm::Triple::riscv64:
LibDirs.append(begin(RISCV64LibDirs), end(RISCV64LibDirs));
TripleAliases.append(begin(RISCV64Triples), end(RISCV64Triples));
BiarchLibDirs.append(begin(RISCV32LibDirs), end(RISCV32LibDirs));
BiarchTripleAliases.append(begin(RISCV32Triples), end(RISCV32Triples));
break;
case llvm::Triple::sparc:
case llvm::Triple::sparcel:
LibDirs.append(begin(SPARCv8LibDirs), end(SPARCv8LibDirs));
TripleAliases.append(begin(SPARCv8Triples), end(SPARCv8Triples));
BiarchLibDirs.append(begin(SPARCv9LibDirs), end(SPARCv9LibDirs));
BiarchTripleAliases.append(begin(SPARCv9Triples), end(SPARCv9Triples));
break;
case llvm::Triple::sparcv9:
LibDirs.append(begin(SPARCv9LibDirs), end(SPARCv9LibDirs));
TripleAliases.append(begin(SPARCv9Triples), end(SPARCv9Triples));
BiarchLibDirs.append(begin(SPARCv8LibDirs), end(SPARCv8LibDirs));
BiarchTripleAliases.append(begin(SPARCv8Triples), end(SPARCv8Triples));
break;
case llvm::Triple::systemz:
LibDirs.append(begin(SystemZLibDirs), end(SystemZLibDirs));
TripleAliases.append(begin(SystemZTriples), end(SystemZTriples));
break;
default:
// By default, just rely on the standard lib directories and the original
// triple.
break;
}
// Also include the multiarch variant if it's different.
if (TargetTriple.str() != BiarchTriple.str())
BiarchTripleAliases.push_back(BiarchTriple.str());
}
bool Generic_GCC::GCCInstallationDetector::ScanGCCForMultilibs(
const llvm::Triple &TargetTriple, const ArgList &Args,
StringRef Path, bool NeedsBiarchSuffix) {
llvm::Triple::ArchType TargetArch = TargetTriple.getArch();
DetectedMultilibs Detected;
// Android standalone toolchain could have multilibs for ARM and Thumb.
// Debian mips multilibs behave more like the rest of the biarch ones,
// so handle them there
if (isArmOrThumbArch(TargetArch) && TargetTriple.isAndroid()) {
// It should also work without multilibs in a simplified toolchain.
findAndroidArmMultilibs(D, TargetTriple, Path, Args, Detected);
} else if (TargetTriple.isCSKY()) {
findCSKYMultilibs(D, TargetTriple, Path, Args, Detected);
} else if (TargetTriple.isMIPS()) {
if (!findMIPSMultilibs(D, TargetTriple, Path, Args, Detected))
return false;
} else if (TargetTriple.isRISCV()) {
findRISCVMultilibs(D, TargetTriple, Path, Args, Detected);
} else if (isMSP430(TargetArch)) {
findMSP430Multilibs(D, TargetTriple, Path, Args, Detected);
} else if (TargetArch == llvm::Triple::avr) {
// AVR has no multilibs.
} else if (!findBiarchMultilibs(D, TargetTriple, Path, Args,
NeedsBiarchSuffix, Detected)) {
return false;
}
Multilibs = Detected.Multilibs;
SelectedInstallation.SelectedMultilib =
Detected.SelectedMultilibs.empty() ? Multilib()
: Detected.SelectedMultilibs.back();
BiarchSibling = Detected.BiarchSibling;
return true;
}
bool Generic_GCC::GCCInstallationDetector::SelectGCCInstallationDirectory(
const SmallVector<Generic_GCC::GCCInstallCandidate, 3> &Installations,
const ArgList &Args,
Generic_GCC::GCCInstallCandidate &SelectedInstallation) const {
if (Installations.empty())
return false;
SelectedInstallation =
*max_element(Installations, [](const auto &Max, const auto &I) {
return I.Version > Max.Version;
});
// FIXME Start selecting installation with libstdc++ in clang 22,
// using the current way of selecting the installation as a fallback
// only. For now, warn if the installation with libstdc++ differs
// from SelectedInstallation.
const GCCInstallCandidate *InstallWithIncludes = nullptr;
for (const auto &I : Installations) {
if ((!InstallWithIncludes || I.Version > InstallWithIncludes->Version) &&
GCCInstallationHasLibStdcxxIncludePaths(I, Args))
InstallWithIncludes = &I;
}
if (InstallWithIncludes && SelectedInstallation.GCCInstallPath !=
InstallWithIncludes->GCCInstallPath)
D.Diag(diag::warn_drv_gcc_install_dir_libstdcxx)
<< InstallWithIncludes->GCCInstallPath
<< SelectedInstallation.GCCInstallPath;
// TODO Warn if SelectedInstallation does not contain libstdc++ includes
// although compiler flags indicate that it is required (C++ compilation,
// libstdc++ not explicitly disabled).
return true;
}
void Generic_GCC::GCCInstallationDetector::ScanLibDirForGCCTriple(
const llvm::Triple &TargetTriple, const ArgList &Args,
const std::string &LibDir, StringRef CandidateTriple,
bool NeedsBiarchSuffix, bool GCCDirExists, bool GCCCrossDirExists) {
// Locations relative to the system lib directory where GCC's triple-specific
// directories might reside.
struct GCCLibSuffix {
// Path from system lib directory to GCC triple-specific directory.
std::string LibSuffix;
// Path from GCC triple-specific directory back to system lib directory.
// This is one '..' component per component in LibSuffix.
StringRef ReversePath;
// Whether this library suffix is relevant for the triple.
bool Active;
} Suffixes[] = {
// This is the normal place.
{"gcc/" + CandidateTriple.str(), "../..", GCCDirExists},
// Debian puts cross-compilers in gcc-cross.
{"gcc-cross/" + CandidateTriple.str(), "../..", GCCCrossDirExists},
// The Freescale PPC SDK has the gcc libraries in
// <sysroot>/usr/lib/<triple>/x.y.z so have a look there as well. Only do
// this on Freescale triples, though, since some systems put a *lot* of
// files in that location, not just GCC installation data.
{CandidateTriple.str(), "..",
TargetTriple.getVendor() == llvm::Triple::Freescale ||
TargetTriple.getVendor() == llvm::Triple::OpenEmbedded}};
SmallVector<GCCInstallCandidate, 3> Installations;
for (auto &Suffix : Suffixes) {
if (!Suffix.Active)
continue;
StringRef LibSuffix = Suffix.LibSuffix;
std::error_code EC;
for (llvm::vfs::directory_iterator
LI = D.getVFS().dir_begin(LibDir + "/" + LibSuffix, EC),
LE;
!EC && LI != LE; LI = LI.increment(EC)) {
StringRef VersionText = llvm::sys::path::filename(LI->path());
GCCVersion CandidateVersion = GCCVersion::Parse(VersionText);
if (CandidateVersion.Major != -1) // Filter obviously bad entries.
if (!CandidateGCCInstallPaths.insert(std::string(LI->path())).second)
continue; // Saw this path before; no need to look at it again.
if (CandidateVersion.isOlderThan(4, 1, 1))
continue;
if (CandidateVersion <= SelectedInstallation.Version && IsValid)
continue;
if (!ScanGCCForMultilibs(TargetTriple, Args, LI->path(),
NeedsBiarchSuffix))
continue;
GCCInstallCandidate Installation;
Installation.Version = CandidateVersion;
Installation.GCCTriple.setTriple(CandidateTriple);
// FIXME: We hack together the directory name here instead of
// using LI to ensure stable path separators across Windows and
// Linux.
Installation.GCCInstallPath =
(LibDir + "/" + LibSuffix + "/" + VersionText).str();
Installation.GCCParentLibPath =
(Installation.GCCInstallPath + "/../" + Suffix.ReversePath).str();
Installation.SelectedMultilib = getMultilib();
Installations.push_back(Installation);
}
}
IsValid |=
SelectGCCInstallationDirectory(Installations, Args, SelectedInstallation);
}
bool Generic_GCC::GCCInstallationDetector::ScanGentooConfigs(
const llvm::Triple &TargetTriple, const ArgList &Args,
const SmallVectorImpl<StringRef> &CandidateTriples,
const SmallVectorImpl<StringRef> &CandidateBiarchTriples) {
if (!D.getVFS().exists(concat(D.SysRoot, GentooConfigDir)))
return false;
for (StringRef CandidateTriple : CandidateTriples) {
if (ScanGentooGccConfig(TargetTriple, Args, CandidateTriple))
return true;
}
for (StringRef CandidateTriple : CandidateBiarchTriples) {
if (ScanGentooGccConfig(TargetTriple, Args, CandidateTriple, true))
return true;
}
return false;
}
bool Generic_GCC::GCCInstallationDetector::ScanGentooGccConfig(
const llvm::Triple &TargetTriple, const ArgList &Args,
StringRef CandidateTriple, bool NeedsBiarchSuffix) {
llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>> File =
D.getVFS().getBufferForFile(concat(D.SysRoot, GentooConfigDir,
"/config-" + CandidateTriple.str()));
if (File) {
SmallVector<StringRef, 2> Lines;
File.get()->getBuffer().split(Lines, "\n");
for (StringRef Line : Lines) {
Line = Line.trim();
// CURRENT=triple-version
if (!Line.consume_front("CURRENT="))
continue;
// Process the config file pointed to by CURRENT.
llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>> ConfigFile =
D.getVFS().getBufferForFile(
concat(D.SysRoot, GentooConfigDir, "/" + Line));
std::pair<StringRef, StringRef> ActiveVersion = Line.rsplit('-');
// List of paths to scan for libraries.
SmallVector<StringRef, 4> GentooScanPaths;
// Scan the Config file to find installed GCC libraries path.
// Typical content of the GCC config file:
// LDPATH="/usr/lib/gcc/x86_64-pc-linux-gnu/4.9.x:/usr/lib/gcc/
// (continued from previous line) x86_64-pc-linux-gnu/4.9.x/32"
// MANPATH="/usr/share/gcc-data/x86_64-pc-linux-gnu/4.9.x/man"
// INFOPATH="/usr/share/gcc-data/x86_64-pc-linux-gnu/4.9.x/info"
// STDCXX_INCDIR="/usr/lib/gcc/x86_64-pc-linux-gnu/4.9.x/include/g++-v4"
// We are looking for the paths listed in LDPATH=... .
if (ConfigFile) {
SmallVector<StringRef, 2> ConfigLines;
ConfigFile.get()->getBuffer().split(ConfigLines, "\n");
for (StringRef ConfLine : ConfigLines) {
ConfLine = ConfLine.trim();
if (ConfLine.consume_front("LDPATH=")) {
// Drop '"' from front and back if present.
ConfLine.consume_back("\"");
ConfLine.consume_front("\"");
// Get all paths sperated by ':'
ConfLine.split(GentooScanPaths, ':', -1, /*AllowEmpty*/ false);
}
}
}
// Test the path based on the version in /etc/env.d/gcc/config-{tuple}.
std::string basePath = "/usr/lib/gcc/" + ActiveVersion.first.str() + "/"
+ ActiveVersion.second.str();
GentooScanPaths.push_back(StringRef(basePath));
// Scan all paths for GCC libraries.
for (const auto &GentooScanPath : GentooScanPaths) {
std::string GentooPath = concat(D.SysRoot, GentooScanPath);
if (D.getVFS().exists(GentooPath + "/crtbegin.o")) {
if (!ScanGCCForMultilibs(TargetTriple, Args, GentooPath,
NeedsBiarchSuffix))
continue;
SelectedInstallation.Version =
GCCVersion::Parse(ActiveVersion.second);
SelectedInstallation.GCCInstallPath = GentooPath;
SelectedInstallation.GCCParentLibPath =
GentooPath + std::string("/../../..");
SelectedInstallation.GCCTriple.setTriple(ActiveVersion.first);
IsValid = true;
return true;
}
}
}
}
return false;
}
Generic_GCC::Generic_GCC(const Driver &D, const llvm::Triple &Triple,
const ArgList &Args)
: ToolChain(D, Triple, Args), GCCInstallation(D),
CudaInstallation(D, Triple, Args), RocmInstallation(D, Triple, Args),
SYCLInstallation(D, Triple, Args) {
getProgramPaths().push_back(getDriver().Dir);
}
Generic_GCC::~Generic_GCC() {}
Tool *Generic_GCC::getTool(Action::ActionClass AC) const {
switch (AC) {
case Action::PreprocessJobClass:
if (!Preprocess)
Preprocess.reset(new clang::driver::tools::gcc::Preprocessor(*this));
return Preprocess.get();
case Action::CompileJobClass:
if (!Compile)
Compile.reset(new tools::gcc::Compiler(*this));
return Compile.get();
default:
return ToolChain::getTool(AC);
}
}
Tool *Generic_GCC::buildAssembler() const {
return new tools::gnutools::Assembler(*this);
}
Tool *Generic_GCC::buildLinker() const { return new tools::gcc::Linker(*this); }
void Generic_GCC::printVerboseInfo(raw_ostream &OS) const {
// Print the information about how we detected the GCC installation.
GCCInstallation.print(OS);
CudaInstallation->print(OS);
RocmInstallation->print(OS);
}
ToolChain::UnwindTableLevel
Generic_GCC::getDefaultUnwindTableLevel(const ArgList &Args) const {
switch (getArch()) {
case llvm::Triple::aarch64:
case llvm::Triple::aarch64_be:
case llvm::Triple::ppc:
case llvm::Triple::ppcle:
case llvm::Triple::ppc64:
case llvm::Triple::ppc64le:
case llvm::Triple::riscv32:
case llvm::Triple::riscv64:
case llvm::Triple::x86:
case llvm::Triple::x86_64:
return UnwindTableLevel::Asynchronous;
default:
return UnwindTableLevel::None;
}
}
bool Generic_GCC::isPICDefault() const {
switch (getArch()) {
case llvm::Triple::x86_64:
return getTriple().isOSWindows();
case llvm::Triple::mips64:
case llvm::Triple::mips64el:
return true;
default:
return false;
}
}
bool Generic_GCC::isPIEDefault(const llvm::opt::ArgList &Args) const {
return false;
}
bool Generic_GCC::isPICDefaultForced() const {
return getArch() == llvm::Triple::x86_64 && getTriple().isOSWindows();
}
bool Generic_GCC::IsIntegratedAssemblerDefault() const {
switch (getTriple().getArch()) {
case llvm::Triple::nvptx:
case llvm::Triple::nvptx64:
case llvm::Triple::xcore:
return false;
default:
return true;
}
}
void Generic_GCC::PushPPaths(ToolChain::path_list &PPaths) {
// Cross-compiling binutils and GCC installations (vanilla and openSUSE at
// least) put various tools in a triple-prefixed directory off of the parent
// of the GCC installation. We use the GCC triple here to ensure that we end
// up with tools that support the same amount of cross compiling as the
// detected GCC installation. For example, if we find a GCC installation
// targeting x86_64, but it is a bi-arch GCC installation, it can also be
// used to target i386.
if (GCCInstallation.isValid()) {
PPaths.push_back(Twine(GCCInstallation.getParentLibPath() + "/../" +
GCCInstallation.getTriple().str() + "/bin")
.str());
}
}
void Generic_GCC::AddMultilibPaths(const Driver &D,
const std::string &SysRoot,
const std::string &OSLibDir,
const std::string &MultiarchTriple,
path_list &Paths) {
// Add the multilib suffixed paths where they are available.
if (GCCInstallation.isValid()) {
assert(!SelectedMultilibs.empty());
const llvm::Triple &GCCTriple = GCCInstallation.getTriple();
const std::string &LibPath =
std::string(GCCInstallation.getParentLibPath());
// Sourcery CodeBench MIPS toolchain holds some libraries under
// a biarch-like suffix of the GCC installation.
if (const auto &PathsCallback = Multilibs.filePathsCallback())
for (const auto &Path : PathsCallback(SelectedMultilibs.back()))
addPathIfExists(D, GCCInstallation.getInstallPath() + Path, Paths);
// Add lib/gcc/$triple/$version, with an optional /multilib suffix.
addPathIfExists(D,
GCCInstallation.getInstallPath() +
SelectedMultilibs.back().gccSuffix(),
Paths);
// Add lib/gcc/$triple/$libdir
// For GCC built with --enable-version-specific-runtime-libs.
addPathIfExists(D, GCCInstallation.getInstallPath() + "/../" + OSLibDir,
Paths);
// GCC cross compiling toolchains will install target libraries which ship
// as part of the toolchain under <prefix>/<triple>/<libdir> rather than as
// any part of the GCC installation in
// <prefix>/<libdir>/gcc/<triple>/<version>. This decision is somewhat
// debatable, but is the reality today. We need to search this tree even
// when we have a sysroot somewhere else. It is the responsibility of
// whomever is doing the cross build targeting a sysroot using a GCC
// installation that is *not* within the system root to ensure two things:
//
// 1) Any DSOs that are linked in from this tree or from the install path
// above must be present on the system root and found via an
// appropriate rpath.
// 2) There must not be libraries installed into
// <prefix>/<triple>/<libdir> unless they should be preferred over
// those within the system root.
//
// Note that this matches the GCC behavior. See the below comment for where
// Clang diverges from GCC's behavior.
addPathIfExists(D,
LibPath + "/../" + GCCTriple.str() + "/lib/../" + OSLibDir +
SelectedMultilibs.back().osSuffix(),
Paths);
// If the GCC installation we found is inside of the sysroot, we want to
// prefer libraries installed in the parent prefix of the GCC installation.
// It is important to *not* use these paths when the GCC installation is
// outside of the system root as that can pick up unintended libraries.
// This usually happens when there is an external cross compiler on the
// host system, and a more minimal sysroot available that is the target of
// the cross. Note that GCC does include some of these directories in some
// configurations but this seems somewhere between questionable and simply
// a bug.
if (StringRef(LibPath).starts_with(SysRoot))
addPathIfExists(D, LibPath + "/../" + OSLibDir, Paths);
}
}
void Generic_GCC::AddMultiarchPaths(const Driver &D,
const std::string &SysRoot,
const std::string &OSLibDir,
path_list &Paths) {
if (GCCInstallation.isValid()) {
const std::string &LibPath =
std::string(GCCInstallation.getParentLibPath());
const llvm::Triple &GCCTriple = GCCInstallation.getTriple();
const Multilib &Multilib = GCCInstallation.getMultilib();
addPathIfExists(
D, LibPath + "/../" + GCCTriple.str() + "/lib" + Multilib.osSuffix(),
Paths);
}
}
void Generic_GCC::AddMultilibIncludeArgs(const ArgList &DriverArgs,
ArgStringList &CC1Args) const {
// Add include directories specific to the selected multilib set and multilib.
if (!GCCInstallation.isValid())
return;
// gcc TOOL_INCLUDE_DIR.
const llvm::Triple &GCCTriple = GCCInstallation.getTriple();
std::string LibPath(GCCInstallation.getParentLibPath());
ToolChain::addSystemInclude(DriverArgs, CC1Args,
Twine(LibPath) + "/../" + GCCTriple.str() +
"/include");
const auto &Callback = Multilibs.includeDirsCallback();
if (Callback) {
for (const auto &Path : Callback(GCCInstallation.getMultilib()))
addExternCSystemIncludeIfExists(DriverArgs, CC1Args,
GCCInstallation.getInstallPath() + Path);
}
}
void Generic_GCC::AddClangCXXStdlibIncludeArgs(const ArgList &DriverArgs,
ArgStringList &CC1Args) const {
if (DriverArgs.hasArg(options::OPT_nostdinc, options::OPT_nostdincxx,
options::OPT_nostdlibinc))
return;
switch (GetCXXStdlibType(DriverArgs)) {
case ToolChain::CST_Libcxx:
addLibCxxIncludePaths(DriverArgs, CC1Args);
break;
case ToolChain::CST_Libstdcxx:
addLibStdCxxIncludePaths(DriverArgs, CC1Args);
break;
}
}
void Generic_GCC::addSYCLIncludeArgs(const ArgList &DriverArgs,
ArgStringList &CC1Args) const {
SYCLInstallation->addSYCLIncludeArgs(DriverArgs, CC1Args);
}
void
Generic_GCC::addLibCxxIncludePaths(const llvm::opt::ArgList &DriverArgs,
llvm::opt::ArgStringList &CC1Args) const {
const Driver &D = getDriver();
std::string SysRoot = computeSysRoot();
if (SysRoot.empty())
SysRoot = llvm::sys::path::get_separator();
auto AddIncludePath = [&](StringRef Path, bool TargetDirRequired = false) {
std::string Version = detectLibcxxVersion(Path);
if (Version.empty())
return false;
// First add the per-target include path if it exists.
bool TargetDirExists = false;
std::optional<std::string> TargetIncludeDir = getTargetSubDirPath(Path);
if (TargetIncludeDir) {
SmallString<128> TargetDir(*TargetIncludeDir);
llvm::sys::path::append(TargetDir, "c++", Version);
if (D.getVFS().exists(TargetDir)) {
addSystemInclude(DriverArgs, CC1Args, TargetDir);
TargetDirExists = true;
}
}
if (TargetDirRequired && !TargetDirExists)
return false;
// Second add the generic one.
SmallString<128> GenericDir(Path);
llvm::sys::path::append(GenericDir, "c++", Version);
addSystemInclude(DriverArgs, CC1Args, GenericDir);
return true;
};
// Android only uses the libc++ headers installed alongside the toolchain if
// they contain an Android-specific target include path, otherwise they're
// incompatible with the NDK libraries.
SmallString<128> DriverIncludeDir(getDriver().Dir);
llvm::sys::path::append(DriverIncludeDir, "..", "include");
if (AddIncludePath(DriverIncludeDir,
/*TargetDirRequired=*/getTriple().isAndroid()))
return;
// If this is a development, non-installed, clang, libcxx will
// not be found at ../include/c++ but it likely to be found at
// one of the following two locations:
SmallString<128> UsrLocalIncludeDir(SysRoot);
llvm::sys::path::append(UsrLocalIncludeDir, "usr", "local", "include");
if (AddIncludePath(UsrLocalIncludeDir))
return;
SmallString<128> UsrIncludeDir(SysRoot);
llvm::sys::path::append(UsrIncludeDir, "usr", "include");
if (AddIncludePath(UsrIncludeDir))
return;
}
static bool addLibStdCXXIncludePaths(llvm::vfs::FileSystem &vfs,
Twine IncludeDir, StringRef Triple,
Twine IncludeSuffix,
const llvm::opt::ArgList &DriverArgs,
llvm::opt::ArgStringList &CC1Args,
bool DetectDebian = false) {
if (!vfs.exists(IncludeDir))
return false;
// Debian native gcc uses g++-multiarch-incdir.diff which uses
// include/x86_64-linux-gnu/c++/10$IncludeSuffix instead of
// include/c++/10/x86_64-linux-gnu$IncludeSuffix.
std::string Dir = IncludeDir.str();
StringRef Include =
llvm::sys::path::parent_path(llvm::sys::path::parent_path(Dir));
std::string Path =
(Include + "/" + Triple + Dir.substr(Include.size()) + IncludeSuffix)
.str();
if (DetectDebian && !vfs.exists(Path))
return false;
// GPLUSPLUS_INCLUDE_DIR
ToolChain::addSystemInclude(DriverArgs, CC1Args, IncludeDir);
// GPLUSPLUS_TOOL_INCLUDE_DIR. If Triple is not empty, add a target-dependent
// include directory.
if (DetectDebian)
ToolChain::addSystemInclude(DriverArgs, CC1Args, Path);
else if (!Triple.empty())
ToolChain::addSystemInclude(DriverArgs, CC1Args,
IncludeDir + "/" + Triple + IncludeSuffix);
// GPLUSPLUS_BACKWARD_INCLUDE_DIR
ToolChain::addSystemInclude(DriverArgs, CC1Args, IncludeDir + "/backward");
return true;
}
bool Generic_GCC::addLibStdCXXIncludePaths(Twine IncludeDir, StringRef Triple,
Twine IncludeSuffix,
const llvm::opt::ArgList &DriverArgs,
llvm::opt::ArgStringList &CC1Args,
bool DetectDebian) const {
return ::addLibStdCXXIncludePaths(getVFS(), IncludeDir, Triple, IncludeSuffix,
DriverArgs, CC1Args, DetectDebian);
}
bool Generic_GCC::GCCInstallCandidate::addGCCLibStdCxxIncludePaths(
llvm::vfs::FileSystem &vfs, const llvm::opt::ArgList &DriverArgs,
llvm::opt::ArgStringList &CC1Args, StringRef DebianMultiarch) const {
// By default, look for the C++ headers in an include directory adjacent to
// the lib directory of the GCC installation. Note that this is expect to be
// equivalent to '/usr/include/c++/X.Y' in almost all cases.
StringRef LibDir = getParentLibPath();
StringRef InstallDir = getInstallPath();
StringRef TripleStr = getTriple().str();
const Multilib &Multilib = getMultilib();
const GCCVersion &Version = getVersion();
// Try /../$triple/include/c++/$version (gcc --print-multiarch is not empty).
if (::addLibStdCXXIncludePaths(
vfs,
LibDir.str() + "/../" + TripleStr + "/include/c++/" + Version.Text,
TripleStr, Multilib.includeSuffix(), DriverArgs, CC1Args))
return true;
// Try /gcc/$triple/$version/include/c++/ (gcc --print-multiarch is not
// empty). Like above but for GCC built with
// --enable-version-specific-runtime-libs.
if (::addLibStdCXXIncludePaths(vfs,
LibDir.str() + "/gcc/" + TripleStr + "/" +
Version.Text + "/include/c++/",
TripleStr, Multilib.includeSuffix(),
DriverArgs, CC1Args))
return true;
// Detect Debian g++-multiarch-incdir.diff.
if (::addLibStdCXXIncludePaths(
vfs, LibDir.str() + "/../include/c++/" + Version.Text,
DebianMultiarch, Multilib.includeSuffix(), DriverArgs, CC1Args,
/*Debian=*/true))
return true;
// Try /../include/c++/$version (gcc --print-multiarch is empty).
if (::addLibStdCXXIncludePaths(
vfs, LibDir.str() + "/../include/c++/" + Version.Text, TripleStr,
Multilib.includeSuffix(), DriverArgs, CC1Args))
return true;
// Otherwise, fall back on a bunch of options which don't use multiarch
// layouts for simplicity.
const std::string LibStdCXXIncludePathCandidates[] = {
// Gentoo is weird and places its headers inside the GCC install,
// so if the first attempt to find the headers fails, try these patterns.
InstallDir.str() + "/include/g++-v" + Version.Text,
InstallDir.str() + "/include/g++-v" + Version.MajorStr + "." +
Version.MinorStr,
InstallDir.str() + "/include/g++-v" + Version.MajorStr,
};
for (const auto &IncludePath : LibStdCXXIncludePathCandidates) {
if (::addLibStdCXXIncludePaths(vfs, IncludePath, TripleStr,
Multilib.includeSuffix(), DriverArgs,
CC1Args))
return true;
}
return false;
}
bool Generic_GCC::GCCInstallationDetector::
GCCInstallationHasLibStdcxxIncludePaths(
const GCCInstallCandidate &GCCInstallation,
const llvm::opt::ArgList &DriverArgs) const {
StringRef DebianMultiarch =
TripleToDebianMultiarch(GCCInstallation.getTriple());
// The following function checks for libstdc++ include paths and
// adds them to the provided argument list. Here we just need the
// check.
llvm::opt::ArgStringList dummyCC1Args;
return GCCInstallation.addGCCLibStdCxxIncludePaths(
D.getVFS(), DriverArgs, dummyCC1Args, DebianMultiarch);
}
bool Generic_GCC::addGCCLibStdCxxIncludePaths(
const llvm::opt::ArgList &DriverArgs,
llvm::opt::ArgStringList &CC1Args) const {
assert(GCCInstallation.isValid());
// Detect Debian g++-multiarch-incdir.diff.
StringRef DebianMultiarch =
GCCInstallation.TripleToDebianMultiarch(GCCInstallation.getTriple());
return GCCInstallation.getSelectedInstallation().addGCCLibStdCxxIncludePaths(
getVFS(), DriverArgs, CC1Args, DebianMultiarch);
}
void
Generic_GCC::addLibStdCxxIncludePaths(const llvm::opt::ArgList &DriverArgs,
llvm::opt::ArgStringList &CC1Args) const {
if (!GCCInstallation.isValid())
return;
GCCInstallation.getSelectedInstallation().addGCCLibStdCxxIncludePaths(
getVFS(), DriverArgs, CC1Args, GCCInstallation.getTriple().str());
}
llvm::opt::DerivedArgList *
Generic_GCC::TranslateArgs(const llvm::opt::DerivedArgList &Args,
StringRef BoundArch,
Action::OffloadKind DeviceOffloadKind) const {
if (DeviceOffloadKind != Action::OFK_SYCL &&
DeviceOffloadKind != Action::OFK_OpenMP)
return nullptr;
DerivedArgList *DAL = new DerivedArgList(Args.getBaseArgs());
// Filter all the arguments we don't care passing to the offloading
// toolchain as they can mess up with the creation of a shared library.
const llvm::DenseSet<unsigned> OpenMPFiltered{
options::OPT_shared, options::OPT_dynamic, options::OPT_static,
options::OPT_fPIE, options::OPT_fno_PIE, options::OPT_fpie,
options::OPT_fno_pie};
for (auto *A : Args)
if (DeviceOffloadKind != Action::OFK_OpenMP ||
!OpenMPFiltered.contains(A->getOption().getID()))
DAL->append(A);
// Request a shared library for CPU offloading. Given that these options
// are decided implicitly, they do not refer to any base argument.
const OptTable &Opts = getDriver().getOpts();
if (DeviceOffloadKind == Action::OFK_OpenMP) {
DAL->AddFlagArg(/*BaseArg=*/nullptr, Opts.getOption(options::OPT_shared));
DAL->AddFlagArg(/*BaseArg=*/nullptr, Opts.getOption(options::OPT_fPIC));
}
// Add the bound architecture to the arguments list if present.
if (!BoundArch.empty()) {
options::ID Opt =
getTriple().isARM() || getTriple().isPPC() || getTriple().isAArch64()
? options::OPT_mcpu_EQ
: options::OPT_march_EQ;
DAL->eraseArg(Opt);
DAL->AddJoinedArg(nullptr, Opts.getOption(Opt), BoundArch);
}
return DAL;
}
void Generic_ELF::anchor() {}
void Generic_ELF::addClangTargetOptions(const ArgList &DriverArgs,
ArgStringList &CC1Args,
Action::OffloadKind) const {
if (!DriverArgs.hasFlag(options::OPT_fuse_init_array,
options::OPT_fno_use_init_array, true))
CC1Args.push_back("-fno-use-init-array");
}