reference, declarationdefinition
definition → references, declarations, derived classes, virtual overrides
reference to multiple definitions → definitions
unreferenced
    1
    2
    3
    4
    5
    6
    7
    8
    9
   10
   11
   12
   13
   14
   15
   16
   17
   18
   19
   20
   21
   22
   23
   24
   25
   26
   27
   28
   29
   30
   31
   32
   33
   34
   35
   36
   37
   38
   39
   40
   41
   42
   43
   44
   45
   46
   47
   48
   49
   50
   51
   52
   53
   54
   55
   56
   57
   58
   59
   60
   61
   62
   63
   64
   65
   66
   67
   68
   69
   70
   71
   72
   73
   74
   75
   76
   77
   78
   79
   80
   81
   82
   83
   84
   85
   86
   87
   88
   89
   90
   91
   92
   93
   94
   95
   96
   97
   98
   99
  100
  101
  102
  103
  104
  105
  106
  107
  108
  109
  110
  111
  112
  113
  114
  115
  116
  117
  118
  119
  120
  121
  122
  123
  124
  125
  126
  127
  128
  129
  130
  131
  132
  133
  134
  135
  136
  137
  138
  139
  140
  141
  142
  143
  144
  145
  146
  147
  148
  149
  150
  151
  152
  153
  154
  155
  156
  157
  158
  159
//===--- AVR.cpp - AVR 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 "AVR.h"
#include "CommonArgs.h"
#include "InputInfo.h"
#include "clang/Driver/Compilation.h"
#include "clang/Driver/DriverDiagnostic.h"
#include "clang/Driver/Options.h"
#include "llvm/ADT/Optional.h"
#include "llvm/ADT/StringSwitch.h"
#include "llvm/MC/MCSubtargetInfo.h"
#include "llvm/MC/SubtargetFeature.h"
#include "llvm/Option/ArgList.h"
#include "llvm/Support/FileSystem.h"

using namespace clang::driver;
using namespace clang::driver::toolchains;
using namespace clang::driver::tools;
using namespace clang;
using namespace llvm::opt;

namespace {

// TODO: Consider merging this into the AVR device table
// array in Targets/AVR.cpp.
llvm::Optional<StringRef> GetMcuFamilyName(StringRef MCU) {
  return llvm::StringSwitch<llvm::Optional<StringRef>>(MCU)
      .Case("atmega328", Optional<StringRef>("avr5"))
      .Case("atmega328p", Optional<StringRef>("avr5"))
      .Default(Optional<StringRef>());
}

const StringRef PossibleAVRLibcLocations[] = {
    "/usr/avr",
    "/usr/lib/avr",
};

} // end anonymous namespace

/// AVR Toolchain
AVRToolChain::AVRToolChain(const Driver &D, const llvm::Triple &Triple,
                           const ArgList &Args)
    : Generic_ELF(D, Triple, Args), LinkStdlib(false) {
  GCCInstallation.init(Triple, Args);

  // Only add default libraries if the user hasn't explicitly opted out.
  if (!Args.hasArg(options::OPT_nostdlib) &&
      !Args.hasArg(options::OPT_nodefaultlibs) &&
      !Args.hasArg(options::OPT_c /* does not apply when not linking */)) {
    std::string CPU = getCPUName(Args, Triple);

    if (CPU.empty()) {
      // We cannot link any standard libraries without an MCU specified.
      D.Diag(diag::warn_drv_avr_mcu_not_specified);
    } else {
      Optional<StringRef> FamilyName = GetMcuFamilyName(CPU);
      Optional<std::string> AVRLibcRoot = findAVRLibcInstallation();

      if (!FamilyName.hasValue()) {
        // We do not have an entry for this CPU in the family
        // mapping table yet.
        D.Diag(diag::warn_drv_avr_family_linking_stdlibs_not_implemented)
            << CPU;
      } else if (!GCCInstallation.isValid()) {
        // No avr-gcc found and so no runtime linked.
        D.Diag(diag::warn_drv_avr_gcc_not_found);
      } else if (!AVRLibcRoot.hasValue()) {
        // No avr-libc found and so no runtime linked.
        D.Diag(diag::warn_drv_avr_libc_not_found);
      } else { // We have enough information to link stdlibs
        std::string GCCRoot = GCCInstallation.getInstallPath();
        std::string LibcRoot = AVRLibcRoot.getValue();

        getFilePaths().push_back(LibcRoot + std::string("/lib/") +
                                 std::string(*FamilyName));
        getFilePaths().push_back(LibcRoot + std::string("/lib/") +
                                 std::string(*FamilyName));
        getFilePaths().push_back(GCCRoot + std::string("/") +
                                 std::string(*FamilyName));

        LinkStdlib = true;
      }
    }

    if (!LinkStdlib)
      D.Diag(diag::warn_drv_avr_stdlib_not_linked);
  }
}

Tool *AVRToolChain::buildLinker() const {
  return new tools::AVR::Linker(getTriple(), *this, LinkStdlib);
}

void AVR::Linker::ConstructJob(Compilation &C, const JobAction &JA,
                               const InputInfo &Output,
                               const InputInfoList &Inputs,
                               const ArgList &Args,
                               const char *LinkingOutput) const {
  // Compute information about the target AVR.
  std::string CPU = getCPUName(Args, getToolChain().getTriple());
  llvm::Optional<StringRef> FamilyName = GetMcuFamilyName(CPU);

  std::string Linker = getToolChain().GetProgramPath(getShortName());
  ArgStringList CmdArgs;
  AddLinkerInputs(getToolChain(), Inputs, Args, CmdArgs, JA);

  CmdArgs.push_back("-o");
  CmdArgs.push_back(Output.getFilename());

  // Enable garbage collection of unused sections.
  CmdArgs.push_back("--gc-sections");

  // Add library search paths before we specify libraries.
  Args.AddAllArgs(CmdArgs, options::OPT_L);
  getToolChain().AddFilePathLibArgs(Args, CmdArgs);

  // If the family name is known, we can link with the device-specific libgcc.
  // Without it, libgcc will simply not be linked. This matches avr-gcc
  // behavior.
  if (LinkStdlib) {
    assert(!CPU.empty() && "CPU name must be known in order to link stdlibs");

    // Add the object file for the CRT.
    std::string CrtFileName = std::string("-l:crt") + CPU + std::string(".o");
    CmdArgs.push_back(Args.MakeArgString(CrtFileName));

    CmdArgs.push_back("-lgcc");
    CmdArgs.push_back("-lm");
    CmdArgs.push_back("-lc");

    // Add the link library specific to the MCU.
    CmdArgs.push_back(Args.MakeArgString(std::string("-l") + CPU));

    // Specify the family name as the emulation mode to use.
    // This is almost always required because otherwise avr-ld
    // will assume 'avr2' and warn about the program being larger
    // than the bare minimum supports.
    CmdArgs.push_back(Args.MakeArgString(std::string("-m") + *FamilyName));
  }

  C.addCommand(std::make_unique<Command>(JA, *this, Args.MakeArgString(Linker),
                                          CmdArgs, Inputs));
}

llvm::Optional<std::string> AVRToolChain::findAVRLibcInstallation() const {
  for (StringRef PossiblePath : PossibleAVRLibcLocations) {
    // Return the first avr-libc installation that exists.
    if (llvm::sys::fs::is_directory(PossiblePath))
      return Optional<std::string>(std::string(PossiblePath));
  }

  return llvm::None;
}