diff options
Diffstat (limited to 'lib/Driver/ToolChains/Arch/Mips.cpp')
-rw-r--r-- | lib/Driver/ToolChains/Arch/Mips.cpp | 403 |
1 files changed, 403 insertions, 0 deletions
diff --git a/lib/Driver/ToolChains/Arch/Mips.cpp b/lib/Driver/ToolChains/Arch/Mips.cpp new file mode 100644 index 0000000000000..cd791af832202 --- /dev/null +++ b/lib/Driver/ToolChains/Arch/Mips.cpp @@ -0,0 +1,403 @@ +//===--- Mips.cpp - Tools Implementations -----------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "Mips.h" +#include "ToolChains/CommonArgs.h" +#include "clang/Driver/Driver.h" +#include "clang/Driver/DriverDiagnostic.h" +#include "clang/Driver/Options.h" +#include "llvm/ADT/StringSwitch.h" +#include "llvm/Option/ArgList.h" + +using namespace clang::driver; +using namespace clang::driver::tools; +using namespace clang; +using namespace llvm::opt; + +bool tools::isMipsArch(llvm::Triple::ArchType Arch) { + return Arch == llvm::Triple::mips || Arch == llvm::Triple::mipsel || + Arch == llvm::Triple::mips64 || Arch == llvm::Triple::mips64el; +} + +// Get CPU and ABI names. They are not independent +// so we have to calculate them together. +void mips::getMipsCPUAndABI(const ArgList &Args, const llvm::Triple &Triple, + StringRef &CPUName, StringRef &ABIName) { + const char *DefMips32CPU = "mips32r2"; + const char *DefMips64CPU = "mips64r2"; + + // MIPS32r6 is the default for mips(el)?-img-linux-gnu and MIPS64r6 is the + // default for mips64(el)?-img-linux-gnu. + if (Triple.getVendor() == llvm::Triple::ImaginationTechnologies && + Triple.getEnvironment() == llvm::Triple::GNU) { + DefMips32CPU = "mips32r6"; + DefMips64CPU = "mips64r6"; + } + + // MIPS64r6 is the default for Android MIPS64 (mips64el-linux-android). + if (Triple.isAndroid()) { + DefMips32CPU = "mips32"; + DefMips64CPU = "mips64r6"; + } + + // MIPS3 is the default for mips64*-unknown-openbsd. + if (Triple.getOS() == llvm::Triple::OpenBSD) + DefMips64CPU = "mips3"; + + if (Arg *A = Args.getLastArg(clang::driver::options::OPT_march_EQ, + options::OPT_mcpu_EQ)) + CPUName = A->getValue(); + + if (Arg *A = Args.getLastArg(options::OPT_mabi_EQ)) { + ABIName = A->getValue(); + // Convert a GNU style Mips ABI name to the name + // accepted by LLVM Mips backend. + ABIName = llvm::StringSwitch<llvm::StringRef>(ABIName) + .Case("32", "o32") + .Case("64", "n64") + .Default(ABIName); + } + + // Setup default CPU and ABI names. + if (CPUName.empty() && ABIName.empty()) { + switch (Triple.getArch()) { + default: + llvm_unreachable("Unexpected triple arch name"); + case llvm::Triple::mips: + case llvm::Triple::mipsel: + CPUName = DefMips32CPU; + break; + case llvm::Triple::mips64: + case llvm::Triple::mips64el: + CPUName = DefMips64CPU; + break; + } + } + + if (ABIName.empty() && + (Triple.getVendor() == llvm::Triple::MipsTechnologies || + Triple.getVendor() == llvm::Triple::ImaginationTechnologies)) { + ABIName = llvm::StringSwitch<const char *>(CPUName) + .Case("mips1", "o32") + .Case("mips2", "o32") + .Case("mips3", "n64") + .Case("mips4", "n64") + .Case("mips5", "n64") + .Case("mips32", "o32") + .Case("mips32r2", "o32") + .Case("mips32r3", "o32") + .Case("mips32r5", "o32") + .Case("mips32r6", "o32") + .Case("mips64", "n64") + .Case("mips64r2", "n64") + .Case("mips64r3", "n64") + .Case("mips64r5", "n64") + .Case("mips64r6", "n64") + .Case("octeon", "n64") + .Case("p5600", "o32") + .Default(""); + } + + if (ABIName.empty()) { + // Deduce ABI name from the target triple. + if (Triple.getArch() == llvm::Triple::mips || + Triple.getArch() == llvm::Triple::mipsel) + ABIName = "o32"; + else + ABIName = "n64"; + } + + if (CPUName.empty()) { + // Deduce CPU name from ABI name. + CPUName = llvm::StringSwitch<const char *>(ABIName) + .Case("o32", DefMips32CPU) + .Cases("n32", "n64", DefMips64CPU) + .Default(""); + } + + // FIXME: Warn on inconsistent use of -march and -mabi. +} + +std::string mips::getMipsABILibSuffix(const ArgList &Args, + const llvm::Triple &Triple) { + StringRef CPUName, ABIName; + tools::mips::getMipsCPUAndABI(Args, Triple, CPUName, ABIName); + return llvm::StringSwitch<std::string>(ABIName) + .Case("o32", "") + .Case("n32", "32") + .Case("n64", "64"); +} + +// Convert ABI name to the GNU tools acceptable variant. +StringRef mips::getGnuCompatibleMipsABIName(StringRef ABI) { + return llvm::StringSwitch<llvm::StringRef>(ABI) + .Case("o32", "32") + .Case("n64", "64") + .Default(ABI); +} + +// Select the MIPS float ABI as determined by -msoft-float, -mhard-float, +// and -mfloat-abi=. +mips::FloatABI mips::getMipsFloatABI(const Driver &D, const ArgList &Args) { + mips::FloatABI ABI = mips::FloatABI::Invalid; + if (Arg *A = + Args.getLastArg(options::OPT_msoft_float, options::OPT_mhard_float, + options::OPT_mfloat_abi_EQ)) { + if (A->getOption().matches(options::OPT_msoft_float)) + ABI = mips::FloatABI::Soft; + else if (A->getOption().matches(options::OPT_mhard_float)) + ABI = mips::FloatABI::Hard; + else { + ABI = llvm::StringSwitch<mips::FloatABI>(A->getValue()) + .Case("soft", mips::FloatABI::Soft) + .Case("hard", mips::FloatABI::Hard) + .Default(mips::FloatABI::Invalid); + if (ABI == mips::FloatABI::Invalid && !StringRef(A->getValue()).empty()) { + D.Diag(clang::diag::err_drv_invalid_mfloat_abi) << A->getAsString(Args); + ABI = mips::FloatABI::Hard; + } + } + } + + // If unspecified, choose the default based on the platform. + if (ABI == mips::FloatABI::Invalid) { + // Assume "hard", because it's a default value used by gcc. + // When we start to recognize specific target MIPS processors, + // we will be able to select the default more correctly. + ABI = mips::FloatABI::Hard; + } + + assert(ABI != mips::FloatABI::Invalid && "must select an ABI"); + return ABI; +} + +void mips::getMIPSTargetFeatures(const Driver &D, const llvm::Triple &Triple, + const ArgList &Args, + std::vector<StringRef> &Features) { + StringRef CPUName; + StringRef ABIName; + getMipsCPUAndABI(Args, Triple, CPUName, ABIName); + ABIName = getGnuCompatibleMipsABIName(ABIName); + + // Historically, PIC code for MIPS was associated with -mabicalls, a.k.a + // SVR4 abicalls. Static code does not use SVR4 calling sequences. An ABI + // extension was developed by Richard Sandiford & Code Sourcery to support + // static code calling PIC code (CPIC). For O32 and N32 this means we have + // several combinations of PIC/static and abicalls. Pure static, static + // with the CPIC extension, and pure PIC code. + + // At final link time, O32 and N32 with CPIC will have another section + // added to the binary which contains the stub functions to perform + // any fixups required for PIC code. + + // For N64, the situation is more regular: code can either be static + // (non-abicalls) or PIC (abicalls). GCC has traditionally picked PIC code + // code for N64. Since Clang has already built the relocation model portion + // of the commandline, we pick add +noabicalls feature in the N64 static + // case. + + // The is another case to be accounted for: -msym32, which enforces that all + // symbols have 32 bits in size. In this case, N64 can in theory use CPIC + // but it is unsupported. + + // The combinations for N64 are: + // a) Static without abicalls and 64bit symbols. + // b) Static with abicalls and 32bit symbols. + // c) PIC with abicalls and 64bit symbols. + + // For case (a) we need to add +noabicalls for N64. + + bool IsN64 = ABIName == "64"; + bool NonPIC = false; + + Arg *LastPICArg = Args.getLastArg(options::OPT_fPIC, options::OPT_fno_PIC, + options::OPT_fpic, options::OPT_fno_pic, + options::OPT_fPIE, options::OPT_fno_PIE, + options::OPT_fpie, options::OPT_fno_pie); + if (LastPICArg) { + Option O = LastPICArg->getOption(); + NonPIC = + (O.matches(options::OPT_fno_PIC) || O.matches(options::OPT_fno_pic) || + O.matches(options::OPT_fno_PIE) || O.matches(options::OPT_fno_pie)); + } + + if (IsN64 && NonPIC) { + Features.push_back("+noabicalls"); + } else { + AddTargetFeature(Args, Features, options::OPT_mno_abicalls, + options::OPT_mabicalls, "noabicalls"); + } + + mips::FloatABI FloatABI = mips::getMipsFloatABI(D, Args); + if (FloatABI == mips::FloatABI::Soft) { + // FIXME: Note, this is a hack. We need to pass the selected float + // mode to the MipsTargetInfoBase to define appropriate macros there. + // Now it is the only method. + Features.push_back("+soft-float"); + } + + if (Arg *A = Args.getLastArg(options::OPT_mnan_EQ)) { + StringRef Val = StringRef(A->getValue()); + if (Val == "2008") { + if (mips::getSupportedNanEncoding(CPUName) & mips::Nan2008) + Features.push_back("+nan2008"); + else { + Features.push_back("-nan2008"); + D.Diag(diag::warn_target_unsupported_nan2008) << CPUName; + } + } else if (Val == "legacy") { + if (mips::getSupportedNanEncoding(CPUName) & mips::NanLegacy) + Features.push_back("-nan2008"); + else { + Features.push_back("+nan2008"); + D.Diag(diag::warn_target_unsupported_nanlegacy) << CPUName; + } + } else + D.Diag(diag::err_drv_unsupported_option_argument) + << A->getOption().getName() << Val; + } + + AddTargetFeature(Args, Features, options::OPT_msingle_float, + options::OPT_mdouble_float, "single-float"); + AddTargetFeature(Args, Features, options::OPT_mips16, options::OPT_mno_mips16, + "mips16"); + AddTargetFeature(Args, Features, options::OPT_mmicromips, + options::OPT_mno_micromips, "micromips"); + AddTargetFeature(Args, Features, options::OPT_mdsp, options::OPT_mno_dsp, + "dsp"); + AddTargetFeature(Args, Features, options::OPT_mdspr2, options::OPT_mno_dspr2, + "dspr2"); + AddTargetFeature(Args, Features, options::OPT_mmsa, options::OPT_mno_msa, + "msa"); + + // Add the last -mfp32/-mfpxx/-mfp64, if none are given and the ABI is O32 + // pass -mfpxx, or if none are given and fp64a is default, pass fp64 and + // nooddspreg. + if (Arg *A = Args.getLastArg(options::OPT_mfp32, options::OPT_mfpxx, + options::OPT_mfp64)) { + if (A->getOption().matches(options::OPT_mfp32)) + Features.push_back(Args.MakeArgString("-fp64")); + else if (A->getOption().matches(options::OPT_mfpxx)) { + Features.push_back(Args.MakeArgString("+fpxx")); + Features.push_back(Args.MakeArgString("+nooddspreg")); + } else + Features.push_back(Args.MakeArgString("+fp64")); + } else if (mips::shouldUseFPXX(Args, Triple, CPUName, ABIName, FloatABI)) { + Features.push_back(Args.MakeArgString("+fpxx")); + Features.push_back(Args.MakeArgString("+nooddspreg")); + } else if (mips::isFP64ADefault(Triple, CPUName)) { + Features.push_back(Args.MakeArgString("+fp64")); + Features.push_back(Args.MakeArgString("+nooddspreg")); + } + + AddTargetFeature(Args, Features, options::OPT_mno_odd_spreg, + options::OPT_modd_spreg, "nooddspreg"); +} + +mips::NanEncoding mips::getSupportedNanEncoding(StringRef &CPU) { + // Strictly speaking, mips32r2 and mips64r2 are NanLegacy-only since Nan2008 + // was first introduced in Release 3. However, other compilers have + // traditionally allowed it for Release 2 so we should do the same. + return (NanEncoding)llvm::StringSwitch<int>(CPU) + .Case("mips1", NanLegacy) + .Case("mips2", NanLegacy) + .Case("mips3", NanLegacy) + .Case("mips4", NanLegacy) + .Case("mips5", NanLegacy) + .Case("mips32", NanLegacy) + .Case("mips32r2", NanLegacy | Nan2008) + .Case("mips32r3", NanLegacy | Nan2008) + .Case("mips32r5", NanLegacy | Nan2008) + .Case("mips32r6", Nan2008) + .Case("mips64", NanLegacy) + .Case("mips64r2", NanLegacy | Nan2008) + .Case("mips64r3", NanLegacy | Nan2008) + .Case("mips64r5", NanLegacy | Nan2008) + .Case("mips64r6", Nan2008) + .Default(NanLegacy); +} + +bool mips::hasCompactBranches(StringRef &CPU) { + // mips32r6 and mips64r6 have compact branches. + return llvm::StringSwitch<bool>(CPU) + .Case("mips32r6", true) + .Case("mips64r6", true) + .Default(false); +} + +bool mips::hasMipsAbiArg(const ArgList &Args, const char *Value) { + Arg *A = Args.getLastArg(options::OPT_mabi_EQ); + return A && (A->getValue() == StringRef(Value)); +} + +bool mips::isUCLibc(const ArgList &Args) { + Arg *A = Args.getLastArg(options::OPT_m_libc_Group); + return A && A->getOption().matches(options::OPT_muclibc); +} + +bool mips::isNaN2008(const ArgList &Args, const llvm::Triple &Triple) { + if (Arg *NaNArg = Args.getLastArg(options::OPT_mnan_EQ)) + return llvm::StringSwitch<bool>(NaNArg->getValue()) + .Case("2008", true) + .Case("legacy", false) + .Default(false); + + // NaN2008 is the default for MIPS32r6/MIPS64r6. + return llvm::StringSwitch<bool>(getCPUName(Args, Triple)) + .Cases("mips32r6", "mips64r6", true) + .Default(false); + + return false; +} + +bool mips::isFP64ADefault(const llvm::Triple &Triple, StringRef CPUName) { + if (!Triple.isAndroid()) + return false; + + // Android MIPS32R6 defaults to FP64A. + return llvm::StringSwitch<bool>(CPUName) + .Case("mips32r6", true) + .Default(false); +} + +bool mips::isFPXXDefault(const llvm::Triple &Triple, StringRef CPUName, + StringRef ABIName, mips::FloatABI FloatABI) { + if (Triple.getVendor() != llvm::Triple::ImaginationTechnologies && + Triple.getVendor() != llvm::Triple::MipsTechnologies && + !Triple.isAndroid()) + return false; + + if (ABIName != "32") + return false; + + // FPXX shouldn't be used if either -msoft-float or -mfloat-abi=soft is + // present. + if (FloatABI == mips::FloatABI::Soft) + return false; + + return llvm::StringSwitch<bool>(CPUName) + .Cases("mips2", "mips3", "mips4", "mips5", true) + .Cases("mips32", "mips32r2", "mips32r3", "mips32r5", true) + .Cases("mips64", "mips64r2", "mips64r3", "mips64r5", true) + .Default(false); +} + +bool mips::shouldUseFPXX(const ArgList &Args, const llvm::Triple &Triple, + StringRef CPUName, StringRef ABIName, + mips::FloatABI FloatABI) { + bool UseFPXX = isFPXXDefault(Triple, CPUName, ABIName, FloatABI); + + // FPXX shouldn't be used if -msingle-float is present. + if (Arg *A = Args.getLastArg(options::OPT_msingle_float, + options::OPT_mdouble_float)) + if (A->getOption().matches(options::OPT_msingle_float)) + UseFPXX = false; + + return UseFPXX; +} |