#include "X86.h"
#include "ToolChains/CommonArgs.h"
#include "clang/Driver/Driver.h"
#include "clang/Driver/DriverDiagnostic.h"
#include "clang/Driver/Options.h"
#include "llvm/ADT/StringExtras.h"
#include "llvm/ADT/StringMap.h"
#include "llvm/ADT/StringSwitch.h"
#include "llvm/Option/ArgList.h"
#include "llvm/Support/Host.h"
using namespace clang::driver;
using namespace clang::driver::tools;
using namespace clang;
using namespace llvm::opt;
std::string x86::getX86TargetCPU(const Driver &D, const ArgList &Args,
const llvm::Triple &Triple) {
if (const Arg *A = Args.getLastArg(clang::driver::options::OPT_march_EQ)) {
StringRef CPU = A->getValue();
if (CPU != "native")
return std::string(CPU);
CPU = llvm::sys::getHostCPUName();
if (!CPU.empty() && CPU != "generic")
return std::string(CPU);
}
if (const Arg *A = Args.getLastArg(options::OPT__SLASH_arch)) {
llvm::StringMap<StringRef> ArchMap({
{"AVX", "sandybridge"},
{"AVX2", "haswell"},
{"AVX512F", "knl"},
{"AVX512", "skylake-avx512"},
});
if (Triple.getArch() == llvm::Triple::x86) {
ArchMap.insert({
{"IA32", "i386"},
{"SSE", "pentium3"},
{"SSE2", "pentium4"},
});
}
StringRef CPU = ArchMap.lookup(A->getValue());
if (CPU.empty()) {
std::vector<StringRef> ValidArchs{ArchMap.keys().begin(),
ArchMap.keys().end()};
sort(ValidArchs);
D.Diag(diag::warn_drv_invalid_arch_name_with_suggestion)
<< A->getValue() << (Triple.getArch() == llvm::Triple::x86)
<< join(ValidArchs, ", ");
}
return std::string(CPU);
}
if (!Triple.isX86())
return "";
bool Is64Bit = Triple.getArch() == llvm::Triple::x86_64;
if (Triple.isOSDarwin()) {
if (Triple.getArchName() == "x86_64h")
return "core-avx2";
if (Triple.isMacOSX() && !Triple.isOSVersionLT(10, 12))
return "penryn";
if (Triple.isDriverKit())
return "nehalem";
return Is64Bit ? "core2" : "yonah";
}
if (Triple.isPS4())
return "btver2";
if (Triple.isPS5())
return "znver2";
if (Triple.isAndroid())
return Is64Bit ? "x86-64" : "i686";
if (Is64Bit)
return "x86-64";
switch (Triple.getOS()) {
case llvm::Triple::NetBSD:
return "i486";
case llvm::Triple::Haiku:
case llvm::Triple::OpenBSD:
return "i586";
case llvm::Triple::FreeBSD:
return "i686";
default:
return "pentium4";
}
}
void x86::getX86TargetFeatures(const Driver &D, const llvm::Triple &Triple,
const ArgList &Args,
std::vector<StringRef> &Features) {
if (const Arg *A = Args.getLastArg(clang::driver::options::OPT_march_EQ)) {
if (StringRef(A->getValue()) == "native") {
llvm::StringMap<bool> HostFeatures;
if (llvm::sys::getHostCPUFeatures(HostFeatures))
for (auto &F : HostFeatures)
Features.push_back(
Args.MakeArgString((F.second ? "+" : "-") + F.first()));
}
}
if (Triple.getArchName() == "x86_64h") {
Features.push_back("-rdrnd");
Features.push_back("-aes");
Features.push_back("-pclmul");
Features.push_back("-rtm");
Features.push_back("-fsgsbase");
}
const llvm::Triple::ArchType ArchType = Triple.getArch();
if (Triple.isAndroid()) {
if (ArchType == llvm::Triple::x86_64) {
Features.push_back("+sse4.2");
Features.push_back("+popcnt");
Features.push_back("+cx16");
} else
Features.push_back("+ssse3");
}
auto SpectreOpt = clang::driver::options::ID::OPT_INVALID;
if (Args.hasArgNoClaim(options::OPT_mretpoline, options::OPT_mno_retpoline,
options::OPT_mspeculative_load_hardening,
options::OPT_mno_speculative_load_hardening)) {
if (Args.hasFlag(options::OPT_mretpoline, options::OPT_mno_retpoline,
false)) {
Features.push_back("+retpoline-indirect-calls");
Features.push_back("+retpoline-indirect-branches");
SpectreOpt = options::OPT_mretpoline;
} else if (Args.hasFlag(options::OPT_mspeculative_load_hardening,
options::OPT_mno_speculative_load_hardening,
false)) {
Features.push_back("+retpoline-indirect-calls");
SpectreOpt = options::OPT_mspeculative_load_hardening;
}
} else if (Args.hasFlag(options::OPT_mretpoline_external_thunk,
options::OPT_mno_retpoline_external_thunk, false)) {
Features.push_back("+retpoline-indirect-calls");
Features.push_back("+retpoline-indirect-branches");
SpectreOpt = options::OPT_mretpoline_external_thunk;
}
auto LVIOpt = clang::driver::options::ID::OPT_INVALID;
if (Args.hasFlag(options::OPT_mlvi_hardening, options::OPT_mno_lvi_hardening,
false)) {
Features.push_back("+lvi-load-hardening");
Features.push_back("+lvi-cfi"); LVIOpt = options::OPT_mlvi_hardening;
} else if (Args.hasFlag(options::OPT_mlvi_cfi, options::OPT_mno_lvi_cfi,
false)) {
Features.push_back("+lvi-cfi");
LVIOpt = options::OPT_mlvi_cfi;
}
if (Args.hasFlag(options::OPT_m_seses, options::OPT_mno_seses, false)) {
if (LVIOpt == options::OPT_mlvi_hardening)
D.Diag(diag::err_drv_argument_not_allowed_with)
<< D.getOpts().getOptionName(options::OPT_mlvi_hardening)
<< D.getOpts().getOptionName(options::OPT_m_seses);
if (SpectreOpt != clang::driver::options::ID::OPT_INVALID)
D.Diag(diag::err_drv_argument_not_allowed_with)
<< D.getOpts().getOptionName(SpectreOpt)
<< D.getOpts().getOptionName(options::OPT_m_seses);
Features.push_back("+seses");
if (!Args.hasArg(options::OPT_mno_lvi_cfi)) {
Features.push_back("+lvi-cfi");
LVIOpt = options::OPT_mlvi_cfi;
}
}
if (SpectreOpt != clang::driver::options::ID::OPT_INVALID &&
LVIOpt != clang::driver::options::ID::OPT_INVALID) {
D.Diag(diag::err_drv_argument_not_allowed_with)
<< D.getOpts().getOptionName(SpectreOpt)
<< D.getOpts().getOptionName(LVIOpt);
}
for (const Arg *A : Args.filtered(options::OPT_m_x86_Features_Group,
options::OPT_mgeneral_regs_only)) {
StringRef Name = A->getOption().getName();
A->claim();
assert(Name.startswith("m") && "Invalid feature name.");
Name = Name.substr(1);
if (A->getOption().getID() == options::OPT_mgeneral_regs_only) {
Features.insert(Features.end(), {"-x87", "-mmx", "-sse"});
continue;
}
bool IsNegative = Name.startswith("no-");
if (IsNegative)
Name = Name.substr(3);
Features.push_back(Args.MakeArgString((IsNegative ? "-" : "+") + Name));
}
if (Arg *A = Args.getLastArg(options::OPT_mharden_sls_EQ)) {
StringRef Scope = A->getValue();
if (Scope == "all") {
Features.push_back("+harden-sls-ijmp");
Features.push_back("+harden-sls-ret");
} else if (Scope == "return") {
Features.push_back("+harden-sls-ret");
} else if (Scope == "indirect-jmp") {
Features.push_back("+harden-sls-ijmp");
} else if (Scope != "none") {
D.Diag(diag::err_drv_unsupported_option_argument)
<< A->getOption().getName() << Scope;
}
}
}