Index: include/clang/Basic/DiagnosticDriverKinds.td =================================================================== --- include/clang/Basic/DiagnosticDriverKinds.td +++ include/clang/Basic/DiagnosticDriverKinds.td @@ -27,6 +27,25 @@ "invalid arch name '%0', %1">; def err_drv_invalid_riscv_ext_arch_name : Error< "invalid arch name '%0', %1 '%2'">; +def warn_drv_avr_mcu_not_specified : Warning< + "no target microcontroller specified on command line, cannot " + "link standard libraries, please pass -mmcu=">, + InGroup; +def warn_drv_avr_gcc_not_found: Warning< + "no avr-gcc installation can be found on the system, " + "cannot link standard libraries">, + InGroup; +def warn_drv_avr_libc_not_found: Warning< + "no avr-libc installation can be found on the system, " + "cannot link standard libraries">, + InGroup; +def warn_drv_avr_family_linking_stdlibs_not_implemented: Warning< + "support for linking stdlibs for microcontroller '%0' is not implemented">, + InGroup; +def warn_drv_avr_stdlib_not_linked: Warning< + "standard library not linked and so no interrupt vector table or " + "compiler runtime routines will be linked">, + InGroup; def err_drv_cuda_bad_gpu_arch : Error<"Unsupported CUDA gpu architecture: %0">; def err_drv_no_cuda_installation : Error< "cannot find CUDA installation. Provide its path via --cuda-path, or pass " Index: include/clang/Basic/DiagnosticGroups.td =================================================================== --- include/clang/Basic/DiagnosticGroups.td +++ include/clang/Basic/DiagnosticGroups.td @@ -1033,6 +1033,10 @@ // compiling CUDA C/C++ but which is not compatible with the CUDA spec. def CudaCompat : DiagGroup<"cuda-compat">; +// Warnings which cause linking of the runtime libraries like +// libc and the CRT to be skipped. +def AVRRtlibLinkingQuirks : DiagGroup<"avr-rtlib-linking-quirks">; + // A warning group for things that will change semantics in the future. def FutureCompat : DiagGroup<"future-compat">; Index: lib/Driver/ToolChains/AVR.h =================================================================== --- lib/Driver/ToolChains/AVR.h +++ lib/Driver/ToolChains/AVR.h @@ -19,11 +19,21 @@ namespace toolchains { class LLVM_LIBRARY_VISIBILITY AVRToolChain : public Generic_ELF { -protected: - Tool *buildLinker() const override; public: AVRToolChain(const Driver &D, const llvm::Triple &Triple, const llvm::opt::ArgList &Args); + +protected: + Tool *buildLinker() const override; + +private: + /// Whether libgcc, libct, and friends should be linked. + /// + /// This is not done if the user does not specify a + /// microcontroller on the command line. + bool LinkStdlib; + + llvm::Optional findAVRLibcInstallation() const; }; } // end namespace toolchains @@ -32,13 +42,20 @@ namespace AVR { class LLVM_LIBRARY_VISIBILITY Linker : public GnuTool { public: - Linker(const ToolChain &TC) : GnuTool("AVR::Linker", "avr-ld", TC) {} + Linker(const llvm::Triple &Triple, const ToolChain &TC, bool LinkStdlib) + : GnuTool("AVR::Linker", "avr-ld", TC), Triple(Triple), + LinkStdlib(LinkStdlib) {} + bool hasIntegratedCPP() const override { return false; } bool isLinkJob() const override { return true; } void ConstructJob(Compilation &C, const JobAction &JA, const InputInfo &Output, const InputInfoList &Inputs, const llvm::opt::ArgList &TCArgs, const char *LinkingOutput) const override; + +protected: + const llvm::Triple &Triple; + bool LinkStdlib; }; } // end namespace AVR } // end namespace tools Index: lib/Driver/ToolChains/AVR.cpp =================================================================== --- lib/Driver/ToolChains/AVR.cpp +++ lib/Driver/ToolChains/AVR.cpp @@ -10,7 +10,14 @@ #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; @@ -18,12 +25,76 @@ using namespace clang; using namespace llvm::opt; +namespace { + +// TODO: Consider merging this into the AVR device table +// array in Targets/AVR.cpp. +llvm::Optional GetMcuFamilyName(StringRef MCU) { + return llvm::StringSwitch>(MCU) + .Case("atmega328", Optional("avr5")) + .Case("atmega328p", Optional("avr5")) + .Default(Optional()); +} + +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) { } + : 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 FamilyName = GetMcuFamilyName(CPU); + Optional 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(*this); + return new tools::AVR::Linker(getTriple(), *this, LinkStdlib); } void AVR::Linker::ConstructJob(Compilation &C, const JobAction &JA, @@ -31,13 +102,58 @@ 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 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(llvm::make_unique(JA, *this, Args.MakeArgString(Linker), CmdArgs, Inputs)); } -// AVR tools end. + +llvm::Optional 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(PossiblePath)); + } + + return llvm::None; +} Index: lib/Driver/ToolChains/Gnu.cpp =================================================================== --- lib/Driver/ToolChains/Gnu.cpp +++ lib/Driver/ToolChains/Gnu.cpp @@ -1926,6 +1926,9 @@ 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 X86_64LibDirs[] = {"/lib64", "/lib"}; static const char *const X86_64Triples[] = { "x86_64-linux-gnu", "x86_64-unknown-linux-gnu", @@ -2146,6 +2149,10 @@ 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::x86_64: LibDirs.append(begin(X86_64LibDirs), end(X86_64LibDirs)); TripleAliases.append(begin(X86_64Triples), end(X86_64Triples)); @@ -2286,6 +2293,8 @@ 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; Index: test/Driver/avr-link-mcu-family-unimplemented.c =================================================================== --- test/Driver/avr-link-mcu-family-unimplemented.c +++ test/Driver/avr-link-mcu-family-unimplemented.c @@ -0,0 +1,7 @@ +// RUN: %clang -### -target avr -no-canonical-prefixes -save-temps -mmcu=attiny13a %s 2>&1 | FileCheck --check-prefix=WARN %s + +// WARN: warning: support for linking stdlibs for microcontroller 'attiny13a' is not implemented +// WARN: warning: standard library not linked and so no interrupt vector table or compiler runtime routines will be linked + +int main() { return 0; } + Index: test/Driver/avr-link-no-mcu-specified.c =================================================================== --- test/Driver/avr-link-no-mcu-specified.c +++ test/Driver/avr-link-no-mcu-specified.c @@ -0,0 +1,10 @@ +// RUN: %clang -### -target avr -no-canonical-prefixes -save-temps %s 2>&1 | FileCheck --check-prefix=WARN %s +// RUN: %clang -### -target avr -no-canonical-prefixes -save-temps -mmcu=atmega328 %s 2>&1 | FileCheck --check-prefix=NOWARN %s + +// WARN: warning: no target microcontroller specified on command line, cannot link standard libraries, please pass -mmcu= +// WARN: warning: standard library not linked and so no interrupt vector table or compiler runtime routines will be linked + +// NOWARN: main + +int main() { return 0; } + Index: test/Driver/avr-link-nostdlib-nodefaultlibs.c =================================================================== --- test/Driver/avr-link-nostdlib-nodefaultlibs.c +++ test/Driver/avr-link-nostdlib-nodefaultlibs.c @@ -0,0 +1,8 @@ +// RUN: %clang -### -target avr -no-canonical-prefixes -save-temps -mmcu=atmega328 -nostdlib %s 2>&1 | FileCheck %s +// RUN: %clang -### -target avr -no-canonical-prefixes -save-temps -mmcu=atmega328 -nodefaultlibs %s 2>&1 | FileCheck %s + +// nostdlib and nodefaultlibs programs should compile fine. + +// CHECK: main +int main() { return 0; } +