[MSP430] Provide a toolchain description

This is an initial implementation for msp430 toolchain including
-mmcu option support
-mhwmult options support
-integrated-as by default

The toolchain uses msp430-elf-as as a linker and supports msp430-gcc toolchain tree.

Patch by Kristina Bessonova!

Differential Revision: https://reviews.llvm.org/D56658

llvm-svn: 351228
diff --git a/clang/lib/Driver/CMakeLists.txt b/clang/lib/Driver/CMakeLists.txt
index 084176b..4793a1f 100644
--- a/clang/lib/Driver/CMakeLists.txt
+++ b/clang/lib/Driver/CMakeLists.txt
@@ -53,6 +53,7 @@
   ToolChains/MipsLinux.cpp
   ToolChains/MinGW.cpp
   ToolChains/Minix.cpp
+  ToolChains/MSP430.cpp
   ToolChains/MSVC.cpp
   ToolChains/Myriad.cpp
   ToolChains/NaCl.cpp
diff --git a/clang/lib/Driver/Driver.cpp b/clang/lib/Driver/Driver.cpp
index 4186fe2..a784e21 100644
--- a/clang/lib/Driver/Driver.cpp
+++ b/clang/lib/Driver/Driver.cpp
@@ -29,6 +29,7 @@
 #include "ToolChains/Hurd.h"
 #include "ToolChains/Lanai.h"
 #include "ToolChains/Linux.h"
+#include "ToolChains/MSP430.h"
 #include "ToolChains/MSVC.h"
 #include "ToolChains/MinGW.h"
 #include "ToolChains/Minix.h"
@@ -4637,6 +4638,10 @@
       case llvm::Triple::avr:
         TC = llvm::make_unique<toolchains::AVRToolChain>(*this, Target, Args);
         break;
+      case llvm::Triple::msp430:
+        TC =
+            llvm::make_unique<toolchains::MSP430ToolChain>(*this, Target, Args);
+        break;
       case llvm::Triple::riscv32:
       case llvm::Triple::riscv64:
         TC = llvm::make_unique<toolchains::RISCVToolChain>(*this, Target, Args);
diff --git a/clang/lib/Driver/ToolChains/Clang.cpp b/clang/lib/Driver/ToolChains/Clang.cpp
index 4b99992..75f1689 100644
--- a/clang/lib/Driver/ToolChains/Clang.cpp
+++ b/clang/lib/Driver/ToolChains/Clang.cpp
@@ -19,6 +19,7 @@
 #include "AMDGPU.h"
 #include "CommonArgs.h"
 #include "Hexagon.h"
+#include "MSP430.h"
 #include "InputInfo.h"
 #include "PS4CPU.h"
 #include "clang/Basic/CharInfo.h"
@@ -364,6 +365,8 @@
   case llvm::Triple::amdgcn:
     amdgpu::getAMDGPUTargetFeatures(D, Args, Features);
     break;
+  case llvm::Triple::msp430:
+    msp430::getMSP430TargetFeatures(D, Args, Features);
   }
 
   // Find the last of each feature.
diff --git a/clang/lib/Driver/ToolChains/Gnu.cpp b/clang/lib/Driver/ToolChains/Gnu.cpp
index 75a0bfb..e4b3589 100644
--- a/clang/lib/Driver/ToolChains/Gnu.cpp
+++ b/clang/lib/Driver/ToolChains/Gnu.cpp
@@ -878,6 +878,10 @@
   return Arch == llvm::Triple::riscv32 || Arch == llvm::Triple::riscv64;
 }
 
+static bool isMSP430(llvm::Triple::ArchType Arch) {
+  return Arch == llvm::Triple::msp430;
+}
+
 static Multilib makeMultilib(StringRef commonSuffix) {
   return Multilib(commonSuffix, commonSuffix, commonSuffix);
 }
@@ -1423,6 +1427,26 @@
     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());
+  Multilib MSP430Multilib = makeMultilib("/430");
+  // FIXME: when clang starts to support msp430x ISA additional logic
+  // to select between multilib must be implemented
+  // Multilib MSP430xMultilib = makeMultilib("/large");
+
+  Result.Multilibs.push_back(MSP430Multilib);
+  Result.Multilibs.FilterOut(NonExistent);
+
+  Multilib::flags_list Flags;
+  if (Result.Multilibs.select(Flags, Result.SelectedMultilib))
+    return true;
+
+  return false;
+}
+
 static void findRISCVMultilibs(const Driver &D,
                                const llvm::Triple &TargetTriple, StringRef Path,
                                const ArgList &Args, DetectedMultilibs &Result) {
@@ -1911,6 +1935,9 @@
   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-linux-gnu", "powerpc-unknown-linux-gnu", "powerpc-linux-gnuspe",
@@ -2135,6 +2162,10 @@
     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));
@@ -2206,6 +2237,8 @@
       return false;
   } else if (isRISCV(TargetArch)) {
     findRISCVMultilibs(D, TargetTriple, Path, Args, Detected);
+  } else if (isMSP430(TargetArch)) {
+    findMSP430Multilibs(D, TargetTriple, Path, Args, Detected);
   } else if (!findBiarchMultilibs(D, TargetTriple, Path, Args,
                                   NeedsBiarchSuffix, Detected)) {
     return false;
@@ -2474,6 +2507,7 @@
   case llvm::Triple::mipsel:
   case llvm::Triple::mips64:
   case llvm::Triple::mips64el:
+  case llvm::Triple::msp430:
     return true;
   case llvm::Triple::sparc:
   case llvm::Triple::sparcel:
diff --git a/clang/lib/Driver/ToolChains/MSP430.cpp b/clang/lib/Driver/ToolChains/MSP430.cpp
new file mode 100644
index 0000000..b2ff88d
--- /dev/null
+++ b/clang/lib/Driver/ToolChains/MSP430.cpp
@@ -0,0 +1,233 @@
+//===--- MSP430.cpp - MSP430 Helpers for Tools ------------------*- C++ -*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "MSP430.h"
+#include "CommonArgs.h"
+#include "Gnu.h"
+#include "InputInfo.h"
+#include "clang/Driver/Compilation.h"
+#include "clang/Driver/Multilib.h"
+#include "clang/Driver/Options.h"
+#include "llvm/Option/ArgList.h"
+#include "llvm/Support/FileSystem.h"
+#include "llvm/Support/Path.h"
+
+using namespace clang::driver;
+using namespace clang::driver::toolchains;
+using namespace clang::driver::tools;
+using namespace clang;
+using namespace llvm::opt;
+
+static bool isSupportedMCU(const StringRef MCU) {
+  return llvm::StringSwitch<bool>(MCU)
+#define MSP430_MCU(NAME) .Case(NAME, true)
+#include "clang/Basic/MSP430Target.def"
+      .Default(false);
+}
+
+static StringRef getSupportedHWMult(const Arg *MCU) {
+  if (!MCU)
+    return "none";
+
+  return llvm::StringSwitch<StringRef>(MCU->getValue())
+#define MSP430_MCU_FEAT(NAME, HWMULT) .Case(NAME, HWMULT)
+#include "clang/Basic/MSP430Target.def"
+      .Default("none");
+}
+
+static StringRef getHWMultLib(const ArgList &Args) {
+  StringRef HWMult = Args.getLastArgValue(options::OPT_mhwmult_EQ, "auto");
+  if (HWMult == "auto") {
+    HWMult = getSupportedHWMult(Args.getLastArg(options::OPT_mmcu_EQ));
+  }
+
+  return llvm::StringSwitch<StringRef>(HWMult)
+      .Case("16bit", "-lmul_16")
+      .Case("32bit", "-lmul_32")
+      .Case("f5series", "-lmul_f5")
+      .Default("-lmul_none");
+}
+
+void msp430::getMSP430TargetFeatures(const Driver &D, const ArgList &Args,
+                                     std::vector<StringRef> &Features) {
+  const Arg *MCU = Args.getLastArg(options::OPT_mmcu_EQ);
+  if (MCU && !isSupportedMCU(MCU->getValue())) {
+    D.Diag(diag::err_drv_clang_unsupported) << MCU->getValue();
+    return;
+  }
+
+  const Arg *HWMultArg = Args.getLastArg(options::OPT_mhwmult_EQ);
+  if (!MCU && !HWMultArg)
+    return;
+
+  StringRef HWMult = HWMultArg ? HWMultArg->getValue() : "auto";
+  StringRef SupportedHWMult = getSupportedHWMult(MCU);
+
+  if (HWMult == "auto") {
+    // 'auto' - deduce hw multiplier support based on mcu name provided.
+    // If no mcu name is provided, assume no hw multiplier is supported.
+    if (!MCU)
+      D.Diag(clang::diag::warn_drv_msp430_hwmult_no_device);
+    HWMult = SupportedHWMult;
+  }
+
+  if (HWMult == "none") {
+    // 'none' - disable hw multiplier.
+    Features.push_back("-hwmult16");
+    Features.push_back("-hwmult32");
+    Features.push_back("-hwmultf5");
+    return;
+  }
+
+  if (MCU && SupportedHWMult == "none")
+    D.Diag(clang::diag::warn_drv_msp430_hwmult_unsupported) << HWMult;
+  if (MCU && HWMult != SupportedHWMult)
+    D.Diag(clang::diag::warn_drv_msp430_hwmult_mismatch)
+        << SupportedHWMult << HWMult;
+
+  if (HWMult == "16bit") {
+    // '16bit' - for 16-bit only hw multiplier.
+    Features.push_back("+hwmult16");
+  } else if (HWMult == "32bit") {
+    // '32bit' - for 16/32-bit hw multiplier.
+    Features.push_back("+hwmult32");
+  } else if (HWMult == "f5series") {
+    // 'f5series' - for 16/32-bit hw multiplier supported by F5 series mcus.
+    Features.push_back("+hwmultf5");
+  } else {
+    D.Diag(clang::diag::err_drv_unsupported_option_argument)
+        << HWMultArg->getAsString(Args) << HWMult;
+  }
+}
+
+/// MSP430 Toolchain
+MSP430ToolChain::MSP430ToolChain(const Driver &D, const llvm::Triple &Triple,
+                                 const ArgList &Args)
+    : Generic_ELF(D, Triple, Args) {
+
+  StringRef MultilibSuf = "";
+
+  GCCInstallation.init(Triple, Args);
+  if (GCCInstallation.isValid()) {
+    MultilibSuf = GCCInstallation.getMultilib().gccSuffix();
+
+    SmallString<128> GCCBinPath;
+    llvm::sys::path::append(GCCBinPath,
+                            GCCInstallation.getParentLibPath(), "..", "bin");
+    addPathIfExists(D, GCCBinPath, getProgramPaths());
+
+    SmallString<128> GCCRtPath;
+    llvm::sys::path::append(GCCRtPath,
+                            GCCInstallation.getInstallPath(), MultilibSuf);
+    addPathIfExists(D, GCCRtPath, getFilePaths());
+  }
+
+  SmallString<128> SysRootDir(computeSysRoot());
+  llvm::sys::path::append(SysRootDir, "lib", MultilibSuf);
+  addPathIfExists(D, SysRootDir, getFilePaths());
+}
+
+std::string MSP430ToolChain::computeSysRoot() const {
+  if (!getDriver().SysRoot.empty())
+    return getDriver().SysRoot;
+
+  SmallString<128> Dir;
+  if (GCCInstallation.isValid())
+    llvm::sys::path::append(Dir, GCCInstallation.getParentLibPath(), "..",
+                            GCCInstallation.getTriple().str());
+  else
+    llvm::sys::path::append(Dir, getDriver().Dir, "..", getTriple().str());
+
+  return Dir.str();
+}
+
+void MSP430ToolChain::AddClangSystemIncludeArgs(const ArgList &DriverArgs,
+                                                ArgStringList &CC1Args) const {
+  if (DriverArgs.hasArg(options::OPT_nostdinc) ||
+      DriverArgs.hasArg(options::OPT_nostdlibinc))
+    return;
+
+  SmallString<128> Dir(computeSysRoot());
+  llvm::sys::path::append(Dir, "include");
+  addSystemInclude(DriverArgs, CC1Args, Dir.str());
+}
+
+void MSP430ToolChain::addClangTargetOptions(const ArgList &DriverArgs,
+                                            ArgStringList &CC1Args,
+                                            Action::OffloadKind) const {
+  CC1Args.push_back("-nostdsysteminc");
+
+  const auto *MCUArg = DriverArgs.getLastArg(options::OPT_mmcu_EQ);
+  if (!MCUArg)
+    return;
+
+  const StringRef MCU = MCUArg->getValue();
+  if (MCU.startswith("msp430i")) {
+    // 'i' should be in lower case as it's defined in TI MSP430-GCC headers
+    CC1Args.push_back(DriverArgs.MakeArgString(
+        "-D__MSP430i" + MCU.drop_front(7).upper() + "__"));
+  } else {
+    CC1Args.push_back(DriverArgs.MakeArgString("-D__" + MCU.upper() + "__"));
+  }
+}
+
+Tool *MSP430ToolChain::buildLinker() const {
+  return new tools::msp430::Linker(*this);
+}
+
+void msp430::Linker::ConstructJob(Compilation &C, const JobAction &JA,
+                                  const InputInfo &Output,
+                                  const InputInfoList &Inputs,
+                                  const ArgList &Args,
+                                  const char *LinkingOutput) const {
+  const ToolChain &ToolChain = getToolChain();
+  const Driver &D = ToolChain.getDriver();
+  std::string Linker = ToolChain.GetProgramPath(getShortName());
+  ArgStringList CmdArgs;
+
+  if (!D.SysRoot.empty())
+    CmdArgs.push_back(Args.MakeArgString("--sysroot=" + D.SysRoot));
+
+  Args.AddAllArgs(CmdArgs, options::OPT_L);
+  ToolChain.AddFilePathLibArgs(Args, CmdArgs);
+
+  if (!Args.hasArg(options::OPT_T)) {
+    if (const Arg *MCUArg = Args.getLastArg(options::OPT_mmcu_EQ))
+      CmdArgs.push_back(
+          Args.MakeArgString("-T" + StringRef(MCUArg->getValue()) + ".ld"));
+  } else {
+    Args.AddAllArgs(CmdArgs, options::OPT_T);
+  }
+
+  if (!Args.hasArg(options::OPT_nostdlib, options::OPT_nostartfiles)) {
+    CmdArgs.push_back(Args.MakeArgString(ToolChain.GetFilePath("crt0.o")));
+    CmdArgs.push_back(Args.MakeArgString(ToolChain.GetFilePath("crtbegin.o")));
+  }
+
+  AddLinkerInputs(getToolChain(), Inputs, Args, CmdArgs, JA);
+
+  CmdArgs.push_back("--start-group");
+  CmdArgs.push_back(Args.MakeArgString(getHWMultLib(Args)));
+  CmdArgs.push_back("-lgcc");
+  if (!Args.hasArg(options::OPT_nostdlib, options::OPT_nodefaultlibs)) {
+    CmdArgs.push_back("-lc");
+    CmdArgs.push_back("-lcrt");
+    CmdArgs.push_back("-lnosys");
+  }
+  CmdArgs.push_back("--end-group");
+
+  if (!Args.hasArg(options::OPT_nostdlib, options::OPT_nostartfiles)) {
+    CmdArgs.push_back(Args.MakeArgString(ToolChain.GetFilePath("crtend.o")));
+    CmdArgs.push_back(Args.MakeArgString(ToolChain.GetFilePath("crtn.o")));
+  }
+  CmdArgs.push_back("-o");
+  CmdArgs.push_back(Output.getFilename());
+  C.addCommand(llvm::make_unique<Command>(JA, *this, Args.MakeArgString(Linker),
+                                          CmdArgs, Inputs));
+}
diff --git a/clang/lib/Driver/ToolChains/MSP430.h b/clang/lib/Driver/ToolChains/MSP430.h
new file mode 100644
index 0000000..0fdceb7
--- /dev/null
+++ b/clang/lib/Driver/ToolChains/MSP430.h
@@ -0,0 +1,71 @@
+//===--- MSP430.h - MSP430-specific Tool Helpers ----------------*- C++ -*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_LIB_DRIVER_TOOLCHAINS_MSP430_H
+#define LLVM_CLANG_LIB_DRIVER_TOOLCHAINS_MSP430_H
+
+#include "Gnu.h"
+#include "InputInfo.h"
+#include "clang/Driver/Driver.h"
+#include "clang/Driver/DriverDiagnostic.h"
+#include "clang/Driver/Tool.h"
+#include "clang/Driver/ToolChain.h"
+#include "llvm/ADT/StringRef.h"
+#include "llvm/Option/Option.h"
+
+#include <string>
+#include <vector>
+
+namespace clang {
+namespace driver {
+namespace toolchains {
+
+class LLVM_LIBRARY_VISIBILITY MSP430ToolChain : public Generic_ELF {
+public:
+  MSP430ToolChain(const Driver &D, const llvm::Triple &Triple,
+                  const llvm::opt::ArgList &Args);
+  void
+  AddClangSystemIncludeArgs(const llvm::opt::ArgList &DriverArgs,
+                            llvm::opt::ArgStringList &CC1Args) const override;
+  void addClangTargetOptions(const llvm::opt::ArgList &DriverArgs,
+                             llvm::opt::ArgStringList &CC1Args,
+                             Action::OffloadKind) const override;
+
+protected:
+  Tool *buildLinker() const override;
+
+private:
+  std::string computeSysRoot() const;
+};
+
+} // end namespace toolchains
+
+namespace tools {
+namespace msp430 {
+
+class LLVM_LIBRARY_VISIBILITY Linker : public GnuTool {
+public:
+  Linker(const ToolChain &TC)
+      : GnuTool("MSP430::Linker", "msp430-elf-ld", TC) {}
+  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;
+};
+
+void getMSP430TargetFeatures(const Driver &D, const llvm::opt::ArgList &Args,
+                             std::vector<llvm::StringRef> &Features);
+} // end namespace msp430
+} // end namespace tools
+} // end namespace driver
+} // end namespace clang
+
+#endif // LLVM_CLANG_LIB_DRIVER_TOOLCHAINS_MSP430_H