forked from OSchip/llvm-project
509 lines
18 KiB
C++
509 lines
18 KiB
C++
//===--- 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) {
|
|
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 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);
|
|
NonPIC = false;
|
|
}
|
|
|
|
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);
|
|
}
|
|
|
|
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::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);
|
|
}
|