Home | History | Annotate | Line # | Download | only in ToolChains
      1 //===-- BareMetal.cpp - Bare Metal ToolChain --------------------*- C++ -*-===//
      2 //
      3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
      4 // See https://llvm.org/LICENSE.txt for license information.
      5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
      6 //
      7 //===----------------------------------------------------------------------===//
      8 
      9 #include "BareMetal.h"
     10 
     11 #include "CommonArgs.h"
     12 #include "InputInfo.h"
     13 #include "Gnu.h"
     14 
     15 #include "Arch/RISCV.h"
     16 #include "clang/Driver/Compilation.h"
     17 #include "clang/Driver/Driver.h"
     18 #include "clang/Driver/DriverDiagnostic.h"
     19 #include "clang/Driver/Options.h"
     20 #include "llvm/Option/ArgList.h"
     21 #include "llvm/Support/Path.h"
     22 #include "llvm/Support/VirtualFileSystem.h"
     23 #include "llvm/Support/raw_ostream.h"
     24 
     25 using namespace llvm::opt;
     26 using namespace clang;
     27 using namespace clang::driver;
     28 using namespace clang::driver::tools;
     29 using namespace clang::driver::toolchains;
     30 
     31 static Multilib makeMultilib(StringRef commonSuffix) {
     32   return Multilib(commonSuffix, commonSuffix, commonSuffix);
     33 }
     34 
     35 static bool findRISCVMultilibs(const Driver &D,
     36                                const llvm::Triple &TargetTriple,
     37                                const ArgList &Args, DetectedMultilibs &Result) {
     38   Multilib::flags_list Flags;
     39   StringRef Arch = riscv::getRISCVArch(Args, TargetTriple);
     40   StringRef Abi = tools::riscv::getRISCVABI(Args, TargetTriple);
     41 
     42   if (TargetTriple.getArch() == llvm::Triple::riscv64) {
     43     Multilib Imac = makeMultilib("").flag("+march=rv64imac").flag("+mabi=lp64");
     44     Multilib Imafdc = makeMultilib("/rv64imafdc/lp64d")
     45                           .flag("+march=rv64imafdc")
     46                           .flag("+mabi=lp64d");
     47 
     48     // Multilib reuse
     49     bool UseImafdc =
     50         (Arch == "rv64imafdc") || (Arch == "rv64gc"); // gc => imafdc
     51 
     52     addMultilibFlag((Arch == "rv64imac"), "march=rv64imac", Flags);
     53     addMultilibFlag(UseImafdc, "march=rv64imafdc", Flags);
     54     addMultilibFlag(Abi == "lp64", "mabi=lp64", Flags);
     55     addMultilibFlag(Abi == "lp64d", "mabi=lp64d", Flags);
     56 
     57     Result.Multilibs = MultilibSet().Either(Imac, Imafdc);
     58     return Result.Multilibs.select(Flags, Result.SelectedMultilib);
     59   }
     60   if (TargetTriple.getArch() == llvm::Triple::riscv32) {
     61     Multilib Imac =
     62         makeMultilib("").flag("+march=rv32imac").flag("+mabi=ilp32");
     63     Multilib I =
     64         makeMultilib("/rv32i/ilp32").flag("+march=rv32i").flag("+mabi=ilp32");
     65     Multilib Im =
     66         makeMultilib("/rv32im/ilp32").flag("+march=rv32im").flag("+mabi=ilp32");
     67     Multilib Iac = makeMultilib("/rv32iac/ilp32")
     68                        .flag("+march=rv32iac")
     69                        .flag("+mabi=ilp32");
     70     Multilib Imafc = makeMultilib("/rv32imafc/ilp32f")
     71                          .flag("+march=rv32imafc")
     72                          .flag("+mabi=ilp32f");
     73 
     74     // Multilib reuse
     75     bool UseI = (Arch == "rv32i") || (Arch == "rv32ic");    // ic => i
     76     bool UseIm = (Arch == "rv32im") || (Arch == "rv32imc"); // imc => im
     77     bool UseImafc = (Arch == "rv32imafc") || (Arch == "rv32imafdc") ||
     78                     (Arch == "rv32gc"); // imafdc,gc => imafc
     79 
     80     addMultilibFlag(UseI, "march=rv32i", Flags);
     81     addMultilibFlag(UseIm, "march=rv32im", Flags);
     82     addMultilibFlag((Arch == "rv32iac"), "march=rv32iac", Flags);
     83     addMultilibFlag((Arch == "rv32imac"), "march=rv32imac", Flags);
     84     addMultilibFlag(UseImafc, "march=rv32imafc", Flags);
     85     addMultilibFlag(Abi == "ilp32", "mabi=ilp32", Flags);
     86     addMultilibFlag(Abi == "ilp32f", "mabi=ilp32f", Flags);
     87 
     88     Result.Multilibs = MultilibSet().Either(I, Im, Iac, Imac, Imafc);
     89     return Result.Multilibs.select(Flags, Result.SelectedMultilib);
     90   }
     91   return false;
     92 }
     93 
     94 BareMetal::BareMetal(const Driver &D, const llvm::Triple &Triple,
     95                            const ArgList &Args)
     96     : ToolChain(D, Triple, Args) {
     97   getProgramPaths().push_back(getDriver().getInstalledDir());
     98   if (getDriver().getInstalledDir() != getDriver().Dir)
     99     getProgramPaths().push_back(getDriver().Dir);
    100 
    101   findMultilibs(D, Triple, Args);
    102   SmallString<128> SysRoot(computeSysRoot());
    103   if (!SysRoot.empty()) {
    104     llvm::sys::path::append(SysRoot, "lib");
    105     getFilePaths().push_back(std::string(SysRoot));
    106   }
    107 }
    108 
    109 /// Is the triple {arm,thumb}-none-none-{eabi,eabihf} ?
    110 static bool isARMBareMetal(const llvm::Triple &Triple) {
    111   if (Triple.getArch() != llvm::Triple::arm &&
    112       Triple.getArch() != llvm::Triple::thumb)
    113     return false;
    114 
    115   if (Triple.getVendor() != llvm::Triple::UnknownVendor)
    116     return false;
    117 
    118   if (Triple.getOS() != llvm::Triple::UnknownOS)
    119     return false;
    120 
    121   if (Triple.getEnvironment() != llvm::Triple::EABI &&
    122       Triple.getEnvironment() != llvm::Triple::EABIHF)
    123     return false;
    124 
    125   return true;
    126 }
    127 
    128 static bool isRISCVBareMetal(const llvm::Triple &Triple) {
    129   if (Triple.getArch() != llvm::Triple::riscv32 &&
    130       Triple.getArch() != llvm::Triple::riscv64)
    131     return false;
    132 
    133   if (Triple.getVendor() != llvm::Triple::UnknownVendor)
    134     return false;
    135 
    136   if (Triple.getOS() != llvm::Triple::UnknownOS)
    137     return false;
    138 
    139   return Triple.getEnvironmentName() == "elf";
    140 }
    141 
    142 void BareMetal::findMultilibs(const Driver &D, const llvm::Triple &Triple,
    143                               const ArgList &Args) {
    144   DetectedMultilibs Result;
    145   if (isRISCVBareMetal(Triple)) {
    146     if (findRISCVMultilibs(D, Triple, Args, Result)) {
    147       SelectedMultilib = Result.SelectedMultilib;
    148       Multilibs = Result.Multilibs;
    149     }
    150   }
    151 }
    152 
    153 bool BareMetal::handlesTarget(const llvm::Triple &Triple) {
    154   return isARMBareMetal(Triple) || isRISCVBareMetal(Triple);
    155 }
    156 
    157 Tool *BareMetal::buildLinker() const {
    158   return new tools::baremetal::Linker(*this);
    159 }
    160 
    161 std::string BareMetal::getCompilerRTPath() const { return getRuntimesDir(); }
    162 
    163 std::string BareMetal::buildCompilerRTBasename(const llvm::opt::ArgList &,
    164                                                StringRef, FileType,
    165                                                bool) const {
    166   return ("libclang_rt.builtins-" + getTriple().getArchName() + ".a").str();
    167 }
    168 
    169 std::string BareMetal::getRuntimesDir() const {
    170   SmallString<128> Dir(getDriver().ResourceDir);
    171   llvm::sys::path::append(Dir, "lib", "baremetal");
    172   Dir += SelectedMultilib.gccSuffix();
    173   return std::string(Dir.str());
    174 }
    175 
    176 std::string BareMetal::computeSysRoot() const {
    177   if (!getDriver().SysRoot.empty())
    178     return getDriver().SysRoot + SelectedMultilib.osSuffix();
    179 
    180   SmallString<128> SysRootDir;
    181   llvm::sys::path::append(SysRootDir, getDriver().Dir, "../lib/clang-runtimes",
    182                           getDriver().getTargetTriple());
    183 
    184   SysRootDir += SelectedMultilib.osSuffix();
    185   return std::string(SysRootDir);
    186 }
    187 
    188 void BareMetal::AddClangSystemIncludeArgs(const ArgList &DriverArgs,
    189                                           ArgStringList &CC1Args) const {
    190   if (DriverArgs.hasArg(options::OPT_nostdinc))
    191     return;
    192 
    193   if (!DriverArgs.hasArg(options::OPT_nobuiltininc)) {
    194     SmallString<128> Dir(getDriver().ResourceDir);
    195     llvm::sys::path::append(Dir, "include");
    196     addSystemInclude(DriverArgs, CC1Args, Dir.str());
    197   }
    198 
    199   if (!DriverArgs.hasArg(options::OPT_nostdlibinc)) {
    200     SmallString<128> Dir(computeSysRoot());
    201     if (!Dir.empty()) {
    202       llvm::sys::path::append(Dir, "include");
    203       addSystemInclude(DriverArgs, CC1Args, Dir.str());
    204     }
    205   }
    206 }
    207 
    208 void BareMetal::addClangTargetOptions(const ArgList &DriverArgs,
    209                                       ArgStringList &CC1Args,
    210                                       Action::OffloadKind) const {
    211   CC1Args.push_back("-nostdsysteminc");
    212 }
    213 
    214 void BareMetal::AddClangCXXStdlibIncludeArgs(
    215     const ArgList &DriverArgs, ArgStringList &CC1Args) const {
    216   if (DriverArgs.hasArg(options::OPT_nostdinc) ||
    217       DriverArgs.hasArg(options::OPT_nostdlibinc) ||
    218       DriverArgs.hasArg(options::OPT_nostdincxx))
    219     return;
    220 
    221   std::string SysRoot(computeSysRoot());
    222   if (SysRoot.empty())
    223     return;
    224 
    225   switch (GetCXXStdlibType(DriverArgs)) {
    226   case ToolChain::CST_Libcxx: {
    227     SmallString<128> Dir(SysRoot);
    228     llvm::sys::path::append(Dir, "include", "c++", "v1");
    229     addSystemInclude(DriverArgs, CC1Args, Dir.str());
    230     break;
    231   }
    232   case ToolChain::CST_Libstdcxx: {
    233     SmallString<128> Dir(SysRoot);
    234     llvm::sys::path::append(Dir, "include", "c++");
    235     std::error_code EC;
    236     Generic_GCC::GCCVersion Version = {"", -1, -1, -1, "", "", ""};
    237     // Walk the subdirs, and find the one with the newest gcc version:
    238     for (llvm::vfs::directory_iterator
    239              LI = getDriver().getVFS().dir_begin(Dir.str(), EC),
    240              LE;
    241          !EC && LI != LE; LI = LI.increment(EC)) {
    242       StringRef VersionText = llvm::sys::path::filename(LI->path());
    243       auto CandidateVersion = Generic_GCC::GCCVersion::Parse(VersionText);
    244       if (CandidateVersion.Major == -1)
    245         continue;
    246       if (CandidateVersion <= Version)
    247         continue;
    248       Version = CandidateVersion;
    249     }
    250     if (Version.Major == -1)
    251       return;
    252     llvm::sys::path::append(Dir, Version.Text);
    253     addSystemInclude(DriverArgs, CC1Args, Dir.str());
    254     break;
    255   }
    256   }
    257 }
    258 
    259 void BareMetal::AddCXXStdlibLibArgs(const ArgList &Args,
    260                                     ArgStringList &CmdArgs) const {
    261   switch (GetCXXStdlibType(Args)) {
    262   case ToolChain::CST_Libcxx:
    263     CmdArgs.push_back("-lc++");
    264     CmdArgs.push_back("-lc++abi");
    265     break;
    266   case ToolChain::CST_Libstdcxx:
    267     CmdArgs.push_back("-lstdc++");
    268     CmdArgs.push_back("-lsupc++");
    269     break;
    270   }
    271   CmdArgs.push_back("-lunwind");
    272 }
    273 
    274 void BareMetal::AddLinkRuntimeLib(const ArgList &Args,
    275                                   ArgStringList &CmdArgs) const {
    276   ToolChain::RuntimeLibType RLT = GetRuntimeLibType(Args);
    277   switch (RLT) {
    278   case ToolChain::RLT_CompilerRT:
    279     CmdArgs.push_back(
    280         Args.MakeArgString("-lclang_rt.builtins-" + getTriple().getArchName()));
    281     return;
    282   case ToolChain::RLT_Libgcc:
    283     CmdArgs.push_back("-lgcc");
    284     return;
    285   }
    286   llvm_unreachable("Unhandled RuntimeLibType.");
    287 }
    288 
    289 void baremetal::Linker::ConstructJob(Compilation &C, const JobAction &JA,
    290                                      const InputInfo &Output,
    291                                      const InputInfoList &Inputs,
    292                                      const ArgList &Args,
    293                                      const char *LinkingOutput) const {
    294   ArgStringList CmdArgs;
    295 
    296   auto &TC = static_cast<const toolchains::BareMetal&>(getToolChain());
    297 
    298   AddLinkerInputs(TC, Inputs, Args, CmdArgs, JA);
    299 
    300   CmdArgs.push_back("-Bstatic");
    301 
    302   Args.AddAllArgs(CmdArgs, {options::OPT_L, options::OPT_T_Group,
    303                             options::OPT_e, options::OPT_s, options::OPT_t,
    304                             options::OPT_Z_Flag, options::OPT_r});
    305 
    306   TC.AddFilePathLibArgs(Args, CmdArgs);
    307 
    308   CmdArgs.push_back(Args.MakeArgString("-L" + TC.getRuntimesDir()));
    309 
    310   if (TC.ShouldLinkCXXStdlib(Args))
    311     TC.AddCXXStdlibLibArgs(Args, CmdArgs);
    312   if (!Args.hasArg(options::OPT_nostdlib, options::OPT_nodefaultlibs)) {
    313     CmdArgs.push_back("-lc");
    314     CmdArgs.push_back("-lm");
    315 
    316     TC.AddLinkRuntimeLib(Args, CmdArgs);
    317   }
    318 
    319   CmdArgs.push_back("-o");
    320   CmdArgs.push_back(Output.getFilename());
    321 
    322   C.addCommand(std::make_unique<Command>(JA, *this, ResponseFileSupport::None(),
    323                                          Args.MakeArgString(TC.GetLinkerPath()),
    324                                          CmdArgs, Inputs, Output));
    325 }
    326