#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;
void mips::getMipsCPUAndABI(const ArgList &Args, const llvm::Triple &Triple,
StringRef &CPUName, StringRef &ABIName) {
const char *DefMips32CPU = "mips32r2";
const char *DefMips64CPU = "mips64r2";
if (Triple.getVendor() == llvm::Triple::ImaginationTechnologies &&
Triple.isGNUEnvironment()) {
DefMips32CPU = "mips32r6";
DefMips64CPU = "mips64r6";
}
if (Triple.getSubArch() == llvm::Triple::MipsSubArch_r6) {
DefMips32CPU = "mips32r6";
DefMips64CPU = "mips64r6";
}
if (Triple.isAndroid()) {
DefMips32CPU = "mips32";
DefMips64CPU = "mips64r6";
}
if (Triple.isOSOpenBSD())
DefMips64CPU = "mips3";
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();
ABIName = llvm::StringSwitch<llvm::StringRef>(ABIName)
.Case("32", "o32")
.Case("64", "n64")
.Default(ABIName);
}
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()) {
ABIName = Triple.isMIPS32() ? "o32" : "n64";
}
if (CPUName.empty()) {
CPUName = llvm::StringSwitch<const char *>(ABIName)
.Case("o32", DefMips32CPU)
.Cases("n32", "n64", DefMips64CPU)
.Default("");
}
}
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");
}
StringRef mips::getGnuCompatibleMipsABIName(StringRef ABI) {
return llvm::StringSwitch<llvm::StringRef>(ABI)
.Case("o32", "32")
.Case("n64", "64")
.Default(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 (ABI == mips::FloatABI::Invalid) {
if (Triple.isOSFreeBSD()) {
ABI = mips::FloatABI::Soft;
} else {
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);
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) {
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");
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) {
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) {
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 Driver &D, 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);
return llvm::StringSwitch<bool>(getCPUName(D, Args, Triple))
.Cases("mips32r6", "mips64r6", true)
.Default(false);
}
bool mips::isFP64ADefault(const llvm::Triple &Triple, StringRef CPUName) {
if (!Triple.isAndroid())
return false;
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;
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);
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) {
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);
}