//===--- Mips.cpp - Tools 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 "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; // 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.isGNUEnvironment()) { DefMips32CPU = "mips32r6"; DefMips64CPU = "mips64r6"; } if (Triple.getSubArch() == llvm::Triple::MipsSubArch_r6) { 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.isOSOpenBSD()) DefMips64CPU = "mips3"; // MIPS2 is the default for mips(el)?-unknown-freebsd. // MIPS3 is the default for mips64(el)?-unknown-freebsd. if (Triple.isOSFreeBSD()) { DefMips32CPU = "mips2"; 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(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.getEnvironment() == llvm::Triple::GNUABIN32)) ABIName = "n32"; if (ABIName.empty() && (Triple.getVendor() == llvm::Triple::MipsTechnologies || Triple.getVendor() == llvm::Triple::ImaginationTechnologies)) { ABIName = llvm::StringSwitch(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. ABIName = Triple.isMIPS32() ? "o32" : "n64"; } if (CPUName.empty()) { // Deduce CPU name from ABI name. CPUName = llvm::StringSwitch(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(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(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, const llvm::Triple &Triple) { 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(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) { if (Triple.isOSFreeBSD()) { // For FreeBSD, assume "soft" on all flavors of MIPS. ABI = mips::FloatABI::Soft; } else { // 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 &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 IsPIC = false; 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)); IsPIC = (O.matches(options::OPT_fPIC) || O.matches(options::OPT_fpic) || O.matches(options::OPT_fPIE) || O.matches(options::OPT_fpie)); } bool UseAbiCalls = false; Arg *ABICallsArg = Args.getLastArg(options::OPT_mabicalls, options::OPT_mno_abicalls); UseAbiCalls = !ABICallsArg || ABICallsArg->getOption().matches(options::OPT_mabicalls); if (IsN64 && NonPIC && (!ABICallsArg || UseAbiCalls)) { D.Diag(diag::warn_drv_unsupported_pic_with_mabicalls) << LastPICArg->getAsString(Args) << (!ABICallsArg ? 0 : 1); } if (ABICallsArg && !UseAbiCalls && IsPIC) { D.Diag(diag::err_drv_unsupported_noabicalls_pic); } if (!UseAbiCalls) Features.push_back("+noabicalls"); else Features.push_back("-noabicalls"); if (Arg *A = Args.getLastArg(options::OPT_mlong_calls, options::OPT_mno_long_calls)) { if (A->getOption().matches(options::OPT_mno_long_calls)) Features.push_back("-long-calls"); else if (!UseAbiCalls) Features.push_back("+long-calls"); else D.Diag(diag::warn_drv_unsupported_longcalls) << (ABICallsArg ? 0 : 1); } if (Arg *A = Args.getLastArg(options::OPT_mxgot, options::OPT_mno_xgot)) { if (A->getOption().matches(options::OPT_mxgot)) Features.push_back("+xgot"); else Features.push_back("-xgot"); } mips::FloatABI FloatABI = mips::getMipsFloatABI(D, Args, Triple); 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::getIEEE754Standard(CPUName) & mips::Std2008) Features.push_back("+nan2008"); else { Features.push_back("-nan2008"); D.Diag(diag::warn_target_unsupported_nan2008) << CPUName; } } else if (Val == "legacy") { if (mips::getIEEE754Standard(CPUName) & mips::Legacy) 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; } if (Arg *A = Args.getLastArg(options::OPT_mabs_EQ)) { StringRef Val = StringRef(A->getValue()); if (Val == "2008") { if (mips::getIEEE754Standard(CPUName) & mips::Std2008) { Features.push_back("+abs2008"); } else { Features.push_back("-abs2008"); D.Diag(diag::warn_target_unsupported_abs2008) << CPUName; } } else if (Val == "legacy") { if (mips::getIEEE754Standard(CPUName) & mips::Legacy) { Features.push_back("-abs2008"); } else { Features.push_back("+abs2008"); D.Diag(diag::warn_target_unsupported_abslegacy) << 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("-fp64"); else if (A->getOption().matches(options::OPT_mfpxx)) { Features.push_back("+fpxx"); Features.push_back("+nooddspreg"); } else Features.push_back("+fp64"); } else if (mips::shouldUseFPXX(Args, Triple, CPUName, ABIName, FloatABI)) { Features.push_back("+fpxx"); Features.push_back("+nooddspreg"); } else if (mips::isFP64ADefault(Triple, CPUName)) { Features.push_back("+fp64"); Features.push_back("+nooddspreg"); } AddTargetFeature(Args, Features, options::OPT_mno_odd_spreg, options::OPT_modd_spreg, "nooddspreg"); AddTargetFeature(Args, Features, options::OPT_mno_madd4, options::OPT_mmadd4, "nomadd4"); AddTargetFeature(Args, Features, options::OPT_mmt, options::OPT_mno_mt, "mt"); AddTargetFeature(Args, Features, options::OPT_mcrc, options::OPT_mno_crc, "crc"); AddTargetFeature(Args, Features, options::OPT_mvirt, options::OPT_mno_virt, "virt"); AddTargetFeature(Args, Features, options::OPT_mginv, options::OPT_mno_ginv, "ginv"); if (Arg *A = Args.getLastArg(options::OPT_mindirect_jump_EQ)) { StringRef Val = StringRef(A->getValue()); if (Val == "hazard") { Arg *B = Args.getLastArg(options::OPT_mmicromips, options::OPT_mno_micromips); Arg *C = Args.getLastArg(options::OPT_mips16, options::OPT_mno_mips16); if (B && B->getOption().matches(options::OPT_mmicromips)) D.Diag(diag::err_drv_unsupported_indirect_jump_opt) << "hazard" << "micromips"; else if (C && C->getOption().matches(options::OPT_mips16)) D.Diag(diag::err_drv_unsupported_indirect_jump_opt) << "hazard" << "mips16"; else if (mips::supportsIndirectJumpHazardBarrier(CPUName)) Features.push_back("+use-indirect-jump-hazard"); else D.Diag(diag::err_drv_unsupported_indirect_jump_opt) << "hazard" << CPUName; } else D.Diag(diag::err_drv_unknown_indirect_jump_opt) << Val; } } mips::IEEE754Standard mips::getIEEE754Standard(StringRef &CPU) { // Strictly speaking, mips32r2 and mips64r2 do not conform to the // IEEE754-2008 standard. Support for this standard was first introduced // in Release 3. However, other compilers have traditionally allowed it // for Release 2 so we should do the same. return (IEEE754Standard)llvm::StringSwitch(CPU) .Case("mips1", Legacy) .Case("mips2", Legacy) .Case("mips3", Legacy) .Case("mips4", Legacy) .Case("mips5", Legacy) .Case("mips32", Legacy) .Case("mips32r2", Legacy | Std2008) .Case("mips32r3", Legacy | Std2008) .Case("mips32r5", Legacy | Std2008) .Case("mips32r6", Std2008) .Case("mips64", Legacy) .Case("mips64r2", Legacy | Std2008) .Case("mips64r3", Legacy | Std2008) .Case("mips64r5", Legacy | Std2008) .Case("mips64r6", Std2008) .Default(Std2008); } bool mips::hasCompactBranches(StringRef &CPU) { // mips32r6 and mips64r6 have compact branches. return llvm::StringSwitch(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 Driver &D, const ArgList &Args, const llvm::Triple &Triple) { if (Arg *NaNArg = Args.getLastArg(options::OPT_mnan_EQ)) return llvm::StringSwitch(NaNArg->getValue()) .Case("2008", true) .Case("legacy", false) .Default(false); // NaN2008 is the default for MIPS32r6/MIPS64r6. return llvm::StringSwitch(getCPUName(D, Args, Triple)) .Cases("mips32r6", "mips64r6", true) .Default(false); } bool mips::isFP64ADefault(const llvm::Triple &Triple, StringRef CPUName) { if (!Triple.isAndroid()) return false; // Android MIPS32R6 defaults to FP64A. return llvm::StringSwitch(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(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; } bool mips::supportsIndirectJumpHazardBarrier(StringRef &CPU) { // Supporting the hazard barrier method of dealing with indirect // jumps requires MIPSR2 support. return llvm::StringSwitch(CPU) .Case("mips32r2", true) .Case("mips32r3", true) .Case("mips32r5", true) .Case("mips32r6", true) .Case("mips64r2", true) .Case("mips64r3", true) .Case("mips64r5", true) .Case("mips64r6", true) .Case("octeon", true) .Case("p5600", true) .Default(false); }