317 lines · cpp
1//===--- X86.cpp - X86 Helpers for Tools ------------------------*- C++ -*-===//2//3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.4// See https://llvm.org/LICENSE.txt for license information.5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception6//7//===----------------------------------------------------------------------===//8 9#include "X86.h"10#include "clang/Driver/Driver.h"11#include "clang/Options/Options.h"12#include "llvm/ADT/StringExtras.h"13#include "llvm/ADT/StringMap.h"14#include "llvm/Option/ArgList.h"15#include "llvm/TargetParser/Host.h"16 17using namespace clang::driver;18using namespace clang::driver::tools;19using namespace clang;20using namespace llvm::opt;21 22std::string x86::getX86TargetCPU(const Driver &D, const ArgList &Args,23 const llvm::Triple &Triple) {24 if (const Arg *A = Args.getLastArg(options::OPT_march_EQ)) {25 StringRef CPU = A->getValue();26 if (CPU != "native")27 return std::string(CPU);28 29 // FIXME: Reject attempts to use -march=native unless the target matches30 // the host.31 CPU = llvm::sys::getHostCPUName();32 if (!CPU.empty() && CPU != "generic")33 return std::string(CPU);34 }35 36 if (const Arg *A = Args.getLastArg(options::OPT__SLASH_arch)) {37 // Mapping built by looking at lib/Basic's X86TargetInfo::initFeatureMap().38 // The keys are case-sensitive; this matches link.exe.39 // 32-bit and 64-bit /arch: flags.40 llvm::StringMap<StringRef> ArchMap({41 {"AVX", "sandybridge"},42 {"AVX2", "haswell"},43 {"AVX512F", "knl"},44 {"AVX512", "skylake-avx512"},45 {"AVX10.1", "sapphirerapids"},46 {"AVX10.2", "diamondrapids"},47 });48 if (Triple.getArch() == llvm::Triple::x86) {49 // 32-bit-only /arch: flags.50 ArchMap.insert({51 {"IA32", "i386"},52 {"SSE", "pentium3"},53 {"SSE2", "pentium4"},54 });55 }56 StringRef CPU = ArchMap.lookup(A->getValue());57 if (CPU.empty()) {58 std::vector<StringRef> ValidArchs{ArchMap.keys().begin(),59 ArchMap.keys().end()};60 sort(ValidArchs);61 D.Diag(diag::warn_drv_invalid_arch_name_with_suggestion)62 << A->getValue() << (Triple.getArch() == llvm::Triple::x86)63 << join(ValidArchs, ", ");64 }65 return std::string(CPU);66 }67 68 // Select the default CPU if none was given (or detection failed).69 70 if (!Triple.isX86())71 return ""; // This routine is only handling x86 targets.72 73 bool Is64Bit = Triple.getArch() == llvm::Triple::x86_64;74 75 // FIXME: Need target hooks.76 if (Triple.isOSDarwin()) {77 if (Triple.getArchName() == "x86_64h")78 return "core-avx2";79 // macosx10.12 drops support for all pre-Penryn Macs.80 // Simulators can still run on 10.11 though, like Xcode.81 if (Triple.isMacOSX() && !Triple.isOSVersionLT(10, 12))82 return "penryn";83 84 if (Triple.isDriverKit())85 return "nehalem";86 87 // The oldest x86_64 Macs have core2/Merom; the oldest x86 Macs have Yonah.88 return Is64Bit ? "core2" : "yonah";89 }90 91 // Set up default CPU name for PS4/PS5 compilers.92 if (Triple.isPS4())93 return "btver2";94 if (Triple.isPS5())95 return "znver2";96 97 // On Android use targets compatible with gcc98 if (Triple.isAndroid())99 return Is64Bit ? "x86-64" : "i686";100 101 // Everything else goes to x86-64 in 64-bit mode.102 if (Is64Bit)103 return "x86-64";104 105 switch (Triple.getOS()) {106 case llvm::Triple::NetBSD:107 return "i486";108 case llvm::Triple::Haiku:109 case llvm::Triple::OpenBSD:110 return "i586";111 case llvm::Triple::FreeBSD:112 return "i686";113 default:114 // Fallback to p4.115 return "pentium4";116 }117}118 119void x86::getX86TargetFeatures(const Driver &D, const llvm::Triple &Triple,120 const ArgList &Args,121 std::vector<StringRef> &Features) {122 // Claim and report unsupported -mabi=. Note: we don't support "sysv_abi" or123 // "ms_abi" as default function attributes.124 if (const Arg *A = Args.getLastArg(options::OPT_mabi_EQ)) {125 StringRef DefaultAbi =126 (Triple.isOSWindows() || Triple.isUEFI()) ? "ms" : "sysv";127 if (A->getValue() != DefaultAbi)128 D.Diag(diag::err_drv_unsupported_opt_for_target)129 << A->getSpelling() << Triple.getTriple();130 }131 132 // If -march=native, autodetect the feature list.133 if (const Arg *A = Args.getLastArg(options::OPT_march_EQ)) {134 if (StringRef(A->getValue()) == "native") {135 for (auto &F : llvm::sys::getHostCPUFeatures())136 Features.push_back(137 Args.MakeArgString((F.second ? "+" : "-") + F.first()));138 }139 }140 141 if (Triple.getArchName() == "x86_64h") {142 // x86_64h implies quite a few of the more modern subtarget features143 // for Haswell class CPUs, but not all of them. Opt-out of a few.144 Features.push_back("-rdrnd");145 Features.push_back("-aes");146 Features.push_back("-pclmul");147 Features.push_back("-rtm");148 Features.push_back("-fsgsbase");149 }150 151 const llvm::Triple::ArchType ArchType = Triple.getArch();152 // Add features to be compatible with gcc for Android.153 if (Triple.isAndroid()) {154 if (ArchType == llvm::Triple::x86_64) {155 Features.push_back("+sse4.2");156 Features.push_back("+popcnt");157 Features.push_back("+cx16");158 } else159 Features.push_back("+ssse3");160 }161 162 // Translate the high level `-mretpoline` flag to the specific target feature163 // flags. We also detect if the user asked for retpoline external thunks but164 // failed to ask for retpolines themselves (through any of the different165 // flags). This is a bit hacky but keeps existing usages working. We should166 // consider deprecating this and instead warn if the user requests external167 // retpoline thunks and *doesn't* request some form of retpolines.168 auto SpectreOpt = options::ID::OPT_INVALID;169 if (Args.hasArgNoClaim(options::OPT_mretpoline, options::OPT_mno_retpoline,170 options::OPT_mspeculative_load_hardening,171 options::OPT_mno_speculative_load_hardening)) {172 if (Args.hasFlag(options::OPT_mretpoline, options::OPT_mno_retpoline,173 false)) {174 Features.push_back("+retpoline-indirect-calls");175 Features.push_back("+retpoline-indirect-branches");176 SpectreOpt = options::OPT_mretpoline;177 } else if (Args.hasFlag(options::OPT_mspeculative_load_hardening,178 options::OPT_mno_speculative_load_hardening,179 false)) {180 // On x86, speculative load hardening relies on at least using retpolines181 // for indirect calls.182 Features.push_back("+retpoline-indirect-calls");183 SpectreOpt = options::OPT_mspeculative_load_hardening;184 }185 } else if (Args.hasFlag(options::OPT_mretpoline_external_thunk,186 options::OPT_mno_retpoline_external_thunk, false)) {187 // FIXME: Add a warning about failing to specify `-mretpoline` and188 // eventually switch to an error here.189 Features.push_back("+retpoline-indirect-calls");190 Features.push_back("+retpoline-indirect-branches");191 SpectreOpt = options::OPT_mretpoline_external_thunk;192 }193 194 auto LVIOpt = options::ID::OPT_INVALID;195 if (Args.hasFlag(options::OPT_mlvi_hardening, options::OPT_mno_lvi_hardening,196 false)) {197 Features.push_back("+lvi-load-hardening");198 Features.push_back("+lvi-cfi"); // load hardening implies CFI protection199 LVIOpt = options::OPT_mlvi_hardening;200 } else if (Args.hasFlag(options::OPT_mlvi_cfi, options::OPT_mno_lvi_cfi,201 false)) {202 Features.push_back("+lvi-cfi");203 LVIOpt = options::OPT_mlvi_cfi;204 }205 206 if (Args.hasFlag(options::OPT_m_seses, options::OPT_mno_seses, false)) {207 if (LVIOpt == options::OPT_mlvi_hardening)208 D.Diag(diag::err_drv_argument_not_allowed_with)209 << D.getOpts().getOptionName(options::OPT_mlvi_hardening)210 << D.getOpts().getOptionName(options::OPT_m_seses);211 212 if (SpectreOpt != options::ID::OPT_INVALID)213 D.Diag(diag::err_drv_argument_not_allowed_with)214 << D.getOpts().getOptionName(SpectreOpt)215 << D.getOpts().getOptionName(options::OPT_m_seses);216 217 Features.push_back("+seses");218 if (!Args.hasArg(options::OPT_mno_lvi_cfi)) {219 Features.push_back("+lvi-cfi");220 LVIOpt = options::OPT_mlvi_cfi;221 }222 }223 224 if (SpectreOpt != options::ID::OPT_INVALID &&225 LVIOpt != options::ID::OPT_INVALID) {226 D.Diag(diag::err_drv_argument_not_allowed_with)227 << D.getOpts().getOptionName(SpectreOpt)228 << D.getOpts().getOptionName(LVIOpt);229 }230 231 // Now add any that the user explicitly requested on the command line,232 // which may override the defaults.233 for (const Arg *A : Args.filtered(options::OPT_m_x86_Features_Group,234 options::OPT_mgeneral_regs_only)) {235 StringRef Name = A->getOption().getName();236 A->claim();237 238 // Skip over "-m".239 assert(Name.starts_with("m") && "Invalid feature name.");240 Name = Name.substr(1);241 242 // Replace -mgeneral-regs-only with -x87, -mmx, -sse243 if (A->getOption().getID() == options::OPT_mgeneral_regs_only) {244 Features.insert(Features.end(), {"-x87", "-mmx", "-sse"});245 continue;246 }247 248 bool IsNegative = Name.starts_with("no-");249 250 bool Not64Bit = ArchType != llvm::Triple::x86_64;251 if (Not64Bit && Name == "uintr")252 D.Diag(diag::err_drv_unsupported_opt_for_target)253 << A->getSpelling() << Triple.getTriple();254 255 if (A->getOption().matches(options::OPT_mapx_features_EQ) ||256 A->getOption().matches(options::OPT_mno_apx_features_EQ)) {257 258 if (Not64Bit && !IsNegative)259 D.Diag(diag::err_drv_unsupported_opt_for_target)260 << StringRef(A->getSpelling().str() + "|-mapxf")261 << Triple.getTriple();262 263 for (StringRef Value : A->getValues()) {264 if (Value != "egpr" && Value != "push2pop2" && Value != "ppx" &&265 Value != "ndd" && Value != "ccmp" && Value != "nf" &&266 Value != "cf" && Value != "zu")267 D.Diag(clang::diag::err_drv_unsupported_option_argument)268 << A->getSpelling() << Value;269 270 Features.push_back(271 Args.MakeArgString((IsNegative ? "-" : "+") + Value));272 }273 continue;274 }275 if (IsNegative)276 Name = Name.substr(3);277 Features.push_back(Args.MakeArgString((IsNegative ? "-" : "+") + Name));278 }279 280 // Enable/disable straight line speculation hardening.281 if (Arg *A = Args.getLastArg(options::OPT_mharden_sls_EQ)) {282 StringRef Scope = A->getValue();283 if (Scope == "all") {284 Features.push_back("+harden-sls-ijmp");285 Features.push_back("+harden-sls-ret");286 } else if (Scope == "return") {287 Features.push_back("+harden-sls-ret");288 } else if (Scope == "indirect-jmp") {289 Features.push_back("+harden-sls-ijmp");290 } else if (Scope != "none") {291 D.Diag(diag::err_drv_unsupported_option_argument)292 << A->getSpelling() << Scope;293 }294 }295 296 // -mno-gather, -mno-scatter support297 if (Args.hasArg(options::OPT_mno_gather))298 Features.push_back("+prefer-no-gather");299 if (Args.hasArg(options::OPT_mno_scatter))300 Features.push_back("+prefer-no-scatter");301 if (Args.hasArg(options::OPT_mapx_inline_asm_use_gpr32))302 Features.push_back("+inline-asm-use-gpr32");303 304 // Warn for removed 3dnow support305 if (const Arg *A =306 Args.getLastArg(options::OPT_m3dnowa, options::OPT_mno_3dnowa,307 options::OPT_mno_3dnow)) {308 if (A->getOption().matches(options::OPT_m3dnowa))309 D.Diag(diag::warn_drv_clang_unsupported) << A->getAsString(Args);310 }311 if (const Arg *A =312 Args.getLastArg(options::OPT_m3dnow, options::OPT_mno_3dnow)) {313 if (A->getOption().matches(options::OPT_m3dnow))314 D.Diag(diag::warn_drv_clang_unsupported) << A->getAsString(Args);315 }316}317