xref: /freebsd/contrib/llvm-project/clang/lib/Driver/ToolChains/AVR.cpp (revision cfd6422a5217410fbd66f7a7a8a64d9d85e61229)
1 //===--- AVR.cpp - AVR ToolChain Implementations ----------------*- 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 "AVR.h"
10 #include "CommonArgs.h"
11 #include "InputInfo.h"
12 #include "clang/Driver/Compilation.h"
13 #include "clang/Driver/DriverDiagnostic.h"
14 #include "clang/Driver/Options.h"
15 #include "llvm/ADT/Optional.h"
16 #include "llvm/ADT/StringSwitch.h"
17 #include "llvm/MC/MCSubtargetInfo.h"
18 #include "llvm/MC/SubtargetFeature.h"
19 #include "llvm/Option/ArgList.h"
20 #include "llvm/Support/FileSystem.h"
21 
22 using namespace clang::driver;
23 using namespace clang::driver::toolchains;
24 using namespace clang::driver::tools;
25 using namespace clang;
26 using namespace llvm::opt;
27 
28 namespace {
29 
30 // TODO: Consider merging this into the AVR device table
31 // array in Targets/AVR.cpp.
32 llvm::Optional<StringRef> GetMcuFamilyName(StringRef MCU) {
33   return llvm::StringSwitch<llvm::Optional<StringRef>>(MCU)
34       .Case("atmega328", Optional<StringRef>("avr5"))
35       .Case("atmega328p", Optional<StringRef>("avr5"))
36       .Default(Optional<StringRef>());
37 }
38 
39 const StringRef PossibleAVRLibcLocations[] = {
40     "/usr/avr",
41     "/usr/lib/avr",
42 };
43 
44 } // end anonymous namespace
45 
46 /// AVR Toolchain
47 AVRToolChain::AVRToolChain(const Driver &D, const llvm::Triple &Triple,
48                            const ArgList &Args)
49     : Generic_ELF(D, Triple, Args), LinkStdlib(false) {
50   GCCInstallation.init(Triple, Args);
51 
52   // Only add default libraries if the user hasn't explicitly opted out.
53   if (!Args.hasArg(options::OPT_nostdlib) &&
54       !Args.hasArg(options::OPT_nodefaultlibs) &&
55       !Args.hasArg(options::OPT_c /* does not apply when not linking */)) {
56     std::string CPU = getCPUName(Args, Triple);
57 
58     if (CPU.empty()) {
59       // We cannot link any standard libraries without an MCU specified.
60       D.Diag(diag::warn_drv_avr_mcu_not_specified);
61     } else {
62       Optional<StringRef> FamilyName = GetMcuFamilyName(CPU);
63       Optional<std::string> AVRLibcRoot = findAVRLibcInstallation();
64 
65       if (!FamilyName.hasValue()) {
66         // We do not have an entry for this CPU in the family
67         // mapping table yet.
68         D.Diag(diag::warn_drv_avr_family_linking_stdlibs_not_implemented)
69             << CPU;
70       } else if (!GCCInstallation.isValid()) {
71         // No avr-gcc found and so no runtime linked.
72         D.Diag(diag::warn_drv_avr_gcc_not_found);
73       } else if (!AVRLibcRoot.hasValue()) {
74         // No avr-libc found and so no runtime linked.
75         D.Diag(diag::warn_drv_avr_libc_not_found);
76       } else { // We have enough information to link stdlibs
77         std::string GCCRoot = std::string(GCCInstallation.getInstallPath());
78         std::string LibcRoot = AVRLibcRoot.getValue();
79 
80         getFilePaths().push_back(LibcRoot + std::string("/lib/") +
81                                  std::string(*FamilyName));
82         getFilePaths().push_back(GCCRoot + std::string("/") +
83                                  std::string(*FamilyName));
84 
85         LinkStdlib = true;
86       }
87     }
88 
89     if (!LinkStdlib)
90       D.Diag(diag::warn_drv_avr_stdlib_not_linked);
91   }
92 }
93 
94 Tool *AVRToolChain::buildLinker() const {
95   return new tools::AVR::Linker(getTriple(), *this, LinkStdlib);
96 }
97 
98 void AVR::Linker::ConstructJob(Compilation &C, const JobAction &JA,
99                                const InputInfo &Output,
100                                const InputInfoList &Inputs,
101                                const ArgList &Args,
102                                const char *LinkingOutput) const {
103   // Compute information about the target AVR.
104   std::string CPU = getCPUName(Args, getToolChain().getTriple());
105   llvm::Optional<StringRef> FamilyName = GetMcuFamilyName(CPU);
106 
107   std::string Linker = getToolChain().GetProgramPath(getShortName());
108   ArgStringList CmdArgs;
109   AddLinkerInputs(getToolChain(), Inputs, Args, CmdArgs, JA);
110 
111   CmdArgs.push_back("-o");
112   CmdArgs.push_back(Output.getFilename());
113 
114   // Enable garbage collection of unused sections.
115   CmdArgs.push_back("--gc-sections");
116 
117   // Add library search paths before we specify libraries.
118   Args.AddAllArgs(CmdArgs, options::OPT_L);
119   getToolChain().AddFilePathLibArgs(Args, CmdArgs);
120 
121   // If the family name is known, we can link with the device-specific libgcc.
122   // Without it, libgcc will simply not be linked. This matches avr-gcc
123   // behavior.
124   if (LinkStdlib) {
125     assert(!CPU.empty() && "CPU name must be known in order to link stdlibs");
126 
127     // Add the object file for the CRT.
128     std::string CrtFileName = std::string("-l:crt") + CPU + std::string(".o");
129     CmdArgs.push_back(Args.MakeArgString(CrtFileName));
130 
131     CmdArgs.push_back("-lgcc");
132     CmdArgs.push_back("-lm");
133     CmdArgs.push_back("-lc");
134 
135     // Add the link library specific to the MCU.
136     CmdArgs.push_back(Args.MakeArgString(std::string("-l") + CPU));
137 
138     // Specify the family name as the emulation mode to use.
139     // This is almost always required because otherwise avr-ld
140     // will assume 'avr2' and warn about the program being larger
141     // than the bare minimum supports.
142     CmdArgs.push_back(Args.MakeArgString(std::string("-m") + *FamilyName));
143   }
144 
145   C.addCommand(
146       std::make_unique<Command>(JA, *this, ResponseFileSupport::AtFileCurCP(),
147                                 Args.MakeArgString(Linker), CmdArgs, Inputs));
148 }
149 
150 llvm::Optional<std::string> AVRToolChain::findAVRLibcInstallation() const {
151   for (StringRef PossiblePath : PossibleAVRLibcLocations) {
152     // Return the first avr-libc installation that exists.
153     if (llvm::sys::fs::is_directory(PossiblePath))
154       return Optional<std::string>(std::string(PossiblePath));
155   }
156 
157   return llvm::None;
158 }
159