| //===--- 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<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.getEnvironment() == llvm::Triple::GNUABIN32)) |
| ABIName = "n32"; |
| |
| 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. |
| ABIName = Triple.isMIPS32() ? "o32" : "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, |
| 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<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) { |
| 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<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 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<int>(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<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; |
| } |
| |
| bool mips::supportsIndirectJumpHazardBarrier(StringRef &CPU) { |
| // Supporting the hazard barrier method of dealing with indirect |
| // jumps requires MIPSR2 support. |
| return llvm::StringSwitch<bool>(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); |
| } |