10b57cec5SDimitry Andric //===--- X86.cpp - X86 Helpers for Tools ------------------------*- C++ -*-===// 20b57cec5SDimitry Andric // 30b57cec5SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 40b57cec5SDimitry Andric // See https://llvm.org/LICENSE.txt for license information. 50b57cec5SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 60b57cec5SDimitry Andric // 70b57cec5SDimitry Andric //===----------------------------------------------------------------------===// 80b57cec5SDimitry Andric 90b57cec5SDimitry Andric #include "X86.h" 100b57cec5SDimitry Andric #include "ToolChains/CommonArgs.h" 110b57cec5SDimitry Andric #include "clang/Driver/Driver.h" 120b57cec5SDimitry Andric #include "clang/Driver/DriverDiagnostic.h" 130b57cec5SDimitry Andric #include "clang/Driver/Options.h" 14349cc55cSDimitry Andric #include "llvm/ADT/StringExtras.h" 15349cc55cSDimitry Andric #include "llvm/ADT/StringMap.h" 160b57cec5SDimitry Andric #include "llvm/Option/ArgList.h" 1706c3fb27SDimitry Andric #include "llvm/TargetParser/Host.h" 180b57cec5SDimitry Andric 190b57cec5SDimitry Andric using namespace clang::driver; 200b57cec5SDimitry Andric using namespace clang::driver::tools; 210b57cec5SDimitry Andric using namespace clang; 220b57cec5SDimitry Andric using namespace llvm::opt; 230b57cec5SDimitry Andric 24349cc55cSDimitry Andric std::string x86::getX86TargetCPU(const Driver &D, const ArgList &Args, 250b57cec5SDimitry Andric const llvm::Triple &Triple) { 260b57cec5SDimitry Andric if (const Arg *A = Args.getLastArg(clang::driver::options::OPT_march_EQ)) { 27e8d8bef9SDimitry Andric StringRef CPU = A->getValue(); 28e8d8bef9SDimitry Andric if (CPU != "native") 29e8d8bef9SDimitry Andric return std::string(CPU); 300b57cec5SDimitry Andric 310b57cec5SDimitry Andric // FIXME: Reject attempts to use -march=native unless the target matches 320b57cec5SDimitry Andric // the host. 33e8d8bef9SDimitry Andric CPU = llvm::sys::getHostCPUName(); 340b57cec5SDimitry Andric if (!CPU.empty() && CPU != "generic") 35e8d8bef9SDimitry Andric return std::string(CPU); 360b57cec5SDimitry Andric } 370b57cec5SDimitry Andric 38349cc55cSDimitry Andric if (const Arg *A = Args.getLastArg(options::OPT__SLASH_arch)) { 390b57cec5SDimitry Andric // Mapping built by looking at lib/Basic's X86TargetInfo::initFeatureMap(). 40349cc55cSDimitry Andric // The keys are case-sensitive; this matches link.exe. 41349cc55cSDimitry Andric // 32-bit and 64-bit /arch: flags. 42349cc55cSDimitry Andric llvm::StringMap<StringRef> ArchMap({ 43349cc55cSDimitry Andric {"AVX", "sandybridge"}, 44349cc55cSDimitry Andric {"AVX2", "haswell"}, 45349cc55cSDimitry Andric {"AVX512F", "knl"}, 46349cc55cSDimitry Andric {"AVX512", "skylake-avx512"}, 47349cc55cSDimitry Andric }); 48349cc55cSDimitry Andric if (Triple.getArch() == llvm::Triple::x86) { 49349cc55cSDimitry Andric // 32-bit-only /arch: flags. 50349cc55cSDimitry Andric ArchMap.insert({ 51349cc55cSDimitry Andric {"IA32", "i386"}, 52349cc55cSDimitry Andric {"SSE", "pentium3"}, 53349cc55cSDimitry Andric {"SSE2", "pentium4"}, 54349cc55cSDimitry Andric }); 550b57cec5SDimitry Andric } 56349cc55cSDimitry Andric StringRef CPU = ArchMap.lookup(A->getValue()); 57349cc55cSDimitry Andric if (CPU.empty()) { 58349cc55cSDimitry Andric std::vector<StringRef> ValidArchs{ArchMap.keys().begin(), 59349cc55cSDimitry Andric ArchMap.keys().end()}; 60349cc55cSDimitry Andric sort(ValidArchs); 61349cc55cSDimitry Andric D.Diag(diag::warn_drv_invalid_arch_name_with_suggestion) 62349cc55cSDimitry Andric << A->getValue() << (Triple.getArch() == llvm::Triple::x86) 63349cc55cSDimitry Andric << join(ValidArchs, ", "); 640b57cec5SDimitry Andric } 65e8d8bef9SDimitry Andric return std::string(CPU); 660b57cec5SDimitry Andric } 670b57cec5SDimitry Andric 680b57cec5SDimitry Andric // Select the default CPU if none was given (or detection failed). 690b57cec5SDimitry Andric 70480093f4SDimitry Andric if (!Triple.isX86()) 71e8d8bef9SDimitry Andric return ""; // This routine is only handling x86 targets. 720b57cec5SDimitry Andric 730b57cec5SDimitry Andric bool Is64Bit = Triple.getArch() == llvm::Triple::x86_64; 740b57cec5SDimitry Andric 750b57cec5SDimitry Andric // FIXME: Need target hooks. 760b57cec5SDimitry Andric if (Triple.isOSDarwin()) { 770b57cec5SDimitry Andric if (Triple.getArchName() == "x86_64h") 780b57cec5SDimitry Andric return "core-avx2"; 790b57cec5SDimitry Andric // macosx10.12 drops support for all pre-Penryn Macs. 800b57cec5SDimitry Andric // Simulators can still run on 10.11 though, like Xcode. 810b57cec5SDimitry Andric if (Triple.isMacOSX() && !Triple.isOSVersionLT(10, 12)) 820b57cec5SDimitry Andric return "penryn"; 8381ad6265SDimitry Andric 8481ad6265SDimitry Andric if (Triple.isDriverKit()) 8581ad6265SDimitry Andric return "nehalem"; 8681ad6265SDimitry Andric 870b57cec5SDimitry Andric // The oldest x86_64 Macs have core2/Merom; the oldest x86 Macs have Yonah. 880b57cec5SDimitry Andric return Is64Bit ? "core2" : "yonah"; 890b57cec5SDimitry Andric } 900b57cec5SDimitry Andric 9181ad6265SDimitry Andric // Set up default CPU name for PS4/PS5 compilers. 9281ad6265SDimitry Andric if (Triple.isPS4()) 930b57cec5SDimitry Andric return "btver2"; 9481ad6265SDimitry Andric if (Triple.isPS5()) 9581ad6265SDimitry Andric return "znver2"; 960b57cec5SDimitry Andric 970b57cec5SDimitry Andric // On Android use targets compatible with gcc 980b57cec5SDimitry Andric if (Triple.isAndroid()) 990b57cec5SDimitry Andric return Is64Bit ? "x86-64" : "i686"; 1000b57cec5SDimitry Andric 1010b57cec5SDimitry Andric // Everything else goes to x86-64 in 64-bit mode. 1020b57cec5SDimitry Andric if (Is64Bit) 1030b57cec5SDimitry Andric return "x86-64"; 1040b57cec5SDimitry Andric 1050b57cec5SDimitry Andric switch (Triple.getOS()) { 1060b57cec5SDimitry Andric case llvm::Triple::NetBSD: 1070b57cec5SDimitry Andric return "i486"; 1080b57cec5SDimitry Andric case llvm::Triple::Haiku: 10975b4d546SDimitry Andric case llvm::Triple::OpenBSD: 1100b57cec5SDimitry Andric return "i586"; 11175b4d546SDimitry Andric case llvm::Triple::FreeBSD: 11275b4d546SDimitry Andric return "i686"; 1130b57cec5SDimitry Andric default: 1140b57cec5SDimitry Andric // Fallback to p4. 1150b57cec5SDimitry Andric return "pentium4"; 1160b57cec5SDimitry Andric } 1170b57cec5SDimitry Andric } 1180b57cec5SDimitry Andric 1190b57cec5SDimitry Andric void x86::getX86TargetFeatures(const Driver &D, const llvm::Triple &Triple, 1200b57cec5SDimitry Andric const ArgList &Args, 1210b57cec5SDimitry Andric std::vector<StringRef> &Features) { 12206c3fb27SDimitry Andric // Claim and report unsupported -mabi=. Note: we don't support "sysv_abi" or 12306c3fb27SDimitry Andric // "ms_abi" as default function attributes. 12406c3fb27SDimitry Andric if (const Arg *A = Args.getLastArg(clang::driver::options::OPT_mabi_EQ)) { 12506c3fb27SDimitry Andric StringRef DefaultAbi = Triple.isOSWindows() ? "ms" : "sysv"; 12606c3fb27SDimitry Andric if (A->getValue() != DefaultAbi) 12706c3fb27SDimitry Andric D.Diag(diag::err_drv_unsupported_opt_for_target) 12806c3fb27SDimitry Andric << A->getSpelling() << Triple.getTriple(); 12906c3fb27SDimitry Andric } 13006c3fb27SDimitry Andric 1310b57cec5SDimitry Andric // If -march=native, autodetect the feature list. 1320b57cec5SDimitry Andric if (const Arg *A = Args.getLastArg(clang::driver::options::OPT_march_EQ)) { 1330b57cec5SDimitry Andric if (StringRef(A->getValue()) == "native") { 134*0fca6ea1SDimitry Andric for (auto &F : llvm::sys::getHostCPUFeatures()) 1350b57cec5SDimitry Andric Features.push_back( 1360b57cec5SDimitry Andric Args.MakeArgString((F.second ? "+" : "-") + F.first())); 1370b57cec5SDimitry Andric } 1380b57cec5SDimitry Andric } 1390b57cec5SDimitry Andric 1400b57cec5SDimitry Andric if (Triple.getArchName() == "x86_64h") { 1410b57cec5SDimitry Andric // x86_64h implies quite a few of the more modern subtarget features 1420b57cec5SDimitry Andric // for Haswell class CPUs, but not all of them. Opt-out of a few. 1430b57cec5SDimitry Andric Features.push_back("-rdrnd"); 1440b57cec5SDimitry Andric Features.push_back("-aes"); 1450b57cec5SDimitry Andric Features.push_back("-pclmul"); 1460b57cec5SDimitry Andric Features.push_back("-rtm"); 1470b57cec5SDimitry Andric Features.push_back("-fsgsbase"); 1480b57cec5SDimitry Andric } 1490b57cec5SDimitry Andric 1500b57cec5SDimitry Andric const llvm::Triple::ArchType ArchType = Triple.getArch(); 1510b57cec5SDimitry Andric // Add features to be compatible with gcc for Android. 1520b57cec5SDimitry Andric if (Triple.isAndroid()) { 1530b57cec5SDimitry Andric if (ArchType == llvm::Triple::x86_64) { 1540b57cec5SDimitry Andric Features.push_back("+sse4.2"); 1550b57cec5SDimitry Andric Features.push_back("+popcnt"); 1560b57cec5SDimitry Andric Features.push_back("+cx16"); 1570b57cec5SDimitry Andric } else 1580b57cec5SDimitry Andric Features.push_back("+ssse3"); 1590b57cec5SDimitry Andric } 1600b57cec5SDimitry Andric 1610b57cec5SDimitry Andric // Translate the high level `-mretpoline` flag to the specific target feature 1620b57cec5SDimitry Andric // flags. We also detect if the user asked for retpoline external thunks but 1630b57cec5SDimitry Andric // failed to ask for retpolines themselves (through any of the different 1640b57cec5SDimitry Andric // flags). This is a bit hacky but keeps existing usages working. We should 1650b57cec5SDimitry Andric // consider deprecating this and instead warn if the user requests external 1660b57cec5SDimitry Andric // retpoline thunks and *doesn't* request some form of retpolines. 1670946e70aSDimitry Andric auto SpectreOpt = clang::driver::options::ID::OPT_INVALID; 1680b57cec5SDimitry Andric if (Args.hasArgNoClaim(options::OPT_mretpoline, options::OPT_mno_retpoline, 1690b57cec5SDimitry Andric options::OPT_mspeculative_load_hardening, 1700b57cec5SDimitry Andric options::OPT_mno_speculative_load_hardening)) { 1710b57cec5SDimitry Andric if (Args.hasFlag(options::OPT_mretpoline, options::OPT_mno_retpoline, 1720b57cec5SDimitry Andric false)) { 1730b57cec5SDimitry Andric Features.push_back("+retpoline-indirect-calls"); 1740b57cec5SDimitry Andric Features.push_back("+retpoline-indirect-branches"); 1750946e70aSDimitry Andric SpectreOpt = options::OPT_mretpoline; 1760b57cec5SDimitry Andric } else if (Args.hasFlag(options::OPT_mspeculative_load_hardening, 1770b57cec5SDimitry Andric options::OPT_mno_speculative_load_hardening, 1780b57cec5SDimitry Andric false)) { 1790b57cec5SDimitry Andric // On x86, speculative load hardening relies on at least using retpolines 1800b57cec5SDimitry Andric // for indirect calls. 1810b57cec5SDimitry Andric Features.push_back("+retpoline-indirect-calls"); 1820946e70aSDimitry Andric SpectreOpt = options::OPT_mspeculative_load_hardening; 1830b57cec5SDimitry Andric } 1840b57cec5SDimitry Andric } else if (Args.hasFlag(options::OPT_mretpoline_external_thunk, 1850b57cec5SDimitry Andric options::OPT_mno_retpoline_external_thunk, false)) { 1860b57cec5SDimitry Andric // FIXME: Add a warning about failing to specify `-mretpoline` and 1870b57cec5SDimitry Andric // eventually switch to an error here. 1880b57cec5SDimitry Andric Features.push_back("+retpoline-indirect-calls"); 1890b57cec5SDimitry Andric Features.push_back("+retpoline-indirect-branches"); 1900946e70aSDimitry Andric SpectreOpt = options::OPT_mretpoline_external_thunk; 1910946e70aSDimitry Andric } 1920946e70aSDimitry Andric 1930946e70aSDimitry Andric auto LVIOpt = clang::driver::options::ID::OPT_INVALID; 1940946e70aSDimitry Andric if (Args.hasFlag(options::OPT_mlvi_hardening, options::OPT_mno_lvi_hardening, 1950946e70aSDimitry Andric false)) { 1960946e70aSDimitry Andric Features.push_back("+lvi-load-hardening"); 1970946e70aSDimitry Andric Features.push_back("+lvi-cfi"); // load hardening implies CFI protection 1980946e70aSDimitry Andric LVIOpt = options::OPT_mlvi_hardening; 1990946e70aSDimitry Andric } else if (Args.hasFlag(options::OPT_mlvi_cfi, options::OPT_mno_lvi_cfi, 2000946e70aSDimitry Andric false)) { 2010946e70aSDimitry Andric Features.push_back("+lvi-cfi"); 2020946e70aSDimitry Andric LVIOpt = options::OPT_mlvi_cfi; 2030946e70aSDimitry Andric } 2040946e70aSDimitry Andric 2055ffd83dbSDimitry Andric if (Args.hasFlag(options::OPT_m_seses, options::OPT_mno_seses, false)) { 2065ffd83dbSDimitry Andric if (LVIOpt == options::OPT_mlvi_hardening) 2075ffd83dbSDimitry Andric D.Diag(diag::err_drv_argument_not_allowed_with) 2085ffd83dbSDimitry Andric << D.getOpts().getOptionName(options::OPT_mlvi_hardening) 2095ffd83dbSDimitry Andric << D.getOpts().getOptionName(options::OPT_m_seses); 2105ffd83dbSDimitry Andric 2115ffd83dbSDimitry Andric if (SpectreOpt != clang::driver::options::ID::OPT_INVALID) 2125ffd83dbSDimitry Andric D.Diag(diag::err_drv_argument_not_allowed_with) 2135ffd83dbSDimitry Andric << D.getOpts().getOptionName(SpectreOpt) 2145ffd83dbSDimitry Andric << D.getOpts().getOptionName(options::OPT_m_seses); 2155ffd83dbSDimitry Andric 2165ffd83dbSDimitry Andric Features.push_back("+seses"); 2175ffd83dbSDimitry Andric if (!Args.hasArg(options::OPT_mno_lvi_cfi)) { 2185ffd83dbSDimitry Andric Features.push_back("+lvi-cfi"); 2195ffd83dbSDimitry Andric LVIOpt = options::OPT_mlvi_cfi; 2205ffd83dbSDimitry Andric } 2215ffd83dbSDimitry Andric } 2225ffd83dbSDimitry Andric 2230946e70aSDimitry Andric if (SpectreOpt != clang::driver::options::ID::OPT_INVALID && 2240946e70aSDimitry Andric LVIOpt != clang::driver::options::ID::OPT_INVALID) { 2250946e70aSDimitry Andric D.Diag(diag::err_drv_argument_not_allowed_with) 2260946e70aSDimitry Andric << D.getOpts().getOptionName(SpectreOpt) 2270946e70aSDimitry Andric << D.getOpts().getOptionName(LVIOpt); 2280b57cec5SDimitry Andric } 2290b57cec5SDimitry Andric 2305f757f3fSDimitry Andric for (const Arg *A : Args.filtered(options::OPT_m_x86_AVX10_Features_Group)) { 2315f757f3fSDimitry Andric StringRef Name = A->getOption().getName(); 2325f757f3fSDimitry Andric A->claim(); 2335f757f3fSDimitry Andric 2345f757f3fSDimitry Andric // Skip over "-m". 2355f757f3fSDimitry Andric assert(Name.starts_with("m") && "Invalid feature name."); 2365f757f3fSDimitry Andric Name = Name.substr(1); 2375f757f3fSDimitry Andric 238647cbc5dSDimitry Andric bool IsNegative = Name.consume_front("no-"); 2395f757f3fSDimitry Andric 2405f757f3fSDimitry Andric #ifndef NDEBUG 2415f757f3fSDimitry Andric assert(Name.starts_with("avx10.") && "Invalid AVX10 feature name."); 2425f757f3fSDimitry Andric StringRef Version, Width; 2435f757f3fSDimitry Andric std::tie(Version, Width) = Name.substr(6).split('-'); 2445f757f3fSDimitry Andric assert(Version == "1" && "Invalid AVX10 feature name."); 2455f757f3fSDimitry Andric assert((Width == "256" || Width == "512") && "Invalid AVX10 feature name."); 2465f757f3fSDimitry Andric #endif 2475f757f3fSDimitry Andric 2485f757f3fSDimitry Andric Features.push_back(Args.MakeArgString((IsNegative ? "-" : "+") + Name)); 2495f757f3fSDimitry Andric } 2505f757f3fSDimitry Andric 2510b57cec5SDimitry Andric // Now add any that the user explicitly requested on the command line, 2520b57cec5SDimitry Andric // which may override the defaults. 253fe6060f1SDimitry Andric for (const Arg *A : Args.filtered(options::OPT_m_x86_Features_Group, 254fe6060f1SDimitry Andric options::OPT_mgeneral_regs_only)) { 255fe6060f1SDimitry Andric StringRef Name = A->getOption().getName(); 256fe6060f1SDimitry Andric A->claim(); 257fe6060f1SDimitry Andric 258fe6060f1SDimitry Andric // Skip over "-m". 2595f757f3fSDimitry Andric assert(Name.starts_with("m") && "Invalid feature name."); 260fe6060f1SDimitry Andric Name = Name.substr(1); 261fe6060f1SDimitry Andric 262fe6060f1SDimitry Andric // Replace -mgeneral-regs-only with -x87, -mmx, -sse 263fe6060f1SDimitry Andric if (A->getOption().getID() == options::OPT_mgeneral_regs_only) { 264fe6060f1SDimitry Andric Features.insert(Features.end(), {"-x87", "-mmx", "-sse"}); 265fe6060f1SDimitry Andric continue; 266fe6060f1SDimitry Andric } 267fe6060f1SDimitry Andric 2685f757f3fSDimitry Andric bool IsNegative = Name.starts_with("no-"); 2695f757f3fSDimitry Andric if (A->getOption().matches(options::OPT_mapx_features_EQ) || 2705f757f3fSDimitry Andric A->getOption().matches(options::OPT_mno_apx_features_EQ)) { 2715f757f3fSDimitry Andric 2725f757f3fSDimitry Andric for (StringRef Value : A->getValues()) { 2735f757f3fSDimitry Andric if (Value == "egpr" || Value == "push2pop2" || Value == "ppx" || 274*0fca6ea1SDimitry Andric Value == "ndd" || Value == "ccmp" || Value == "nf" || 275*0fca6ea1SDimitry Andric Value == "cf" || Value == "zu") { 2765f757f3fSDimitry Andric Features.push_back( 2775f757f3fSDimitry Andric Args.MakeArgString((IsNegative ? "-" : "+") + Value)); 2785f757f3fSDimitry Andric continue; 2795f757f3fSDimitry Andric } 2805f757f3fSDimitry Andric D.Diag(clang::diag::err_drv_unsupported_option_argument) 2815f757f3fSDimitry Andric << A->getSpelling() << Value; 2825f757f3fSDimitry Andric } 2835f757f3fSDimitry Andric continue; 2845f757f3fSDimitry Andric } 285fe6060f1SDimitry Andric if (IsNegative) 286fe6060f1SDimitry Andric Name = Name.substr(3); 287fe6060f1SDimitry Andric Features.push_back(Args.MakeArgString((IsNegative ? "-" : "+") + Name)); 288fe6060f1SDimitry Andric } 28981ad6265SDimitry Andric 29081ad6265SDimitry Andric // Enable/disable straight line speculation hardening. 29181ad6265SDimitry Andric if (Arg *A = Args.getLastArg(options::OPT_mharden_sls_EQ)) { 29281ad6265SDimitry Andric StringRef Scope = A->getValue(); 29381ad6265SDimitry Andric if (Scope == "all") { 29481ad6265SDimitry Andric Features.push_back("+harden-sls-ijmp"); 29581ad6265SDimitry Andric Features.push_back("+harden-sls-ret"); 29681ad6265SDimitry Andric } else if (Scope == "return") { 29781ad6265SDimitry Andric Features.push_back("+harden-sls-ret"); 29881ad6265SDimitry Andric } else if (Scope == "indirect-jmp") { 29981ad6265SDimitry Andric Features.push_back("+harden-sls-ijmp"); 30081ad6265SDimitry Andric } else if (Scope != "none") { 30181ad6265SDimitry Andric D.Diag(diag::err_drv_unsupported_option_argument) 302bdd1243dSDimitry Andric << A->getSpelling() << Scope; 30381ad6265SDimitry Andric } 30481ad6265SDimitry Andric } 3058a4dda33SDimitry Andric 3068a4dda33SDimitry Andric // -mno-gather, -mno-scatter support 3078a4dda33SDimitry Andric if (Args.hasArg(options::OPT_mno_gather)) 3088a4dda33SDimitry Andric Features.push_back("+prefer-no-gather"); 3098a4dda33SDimitry Andric if (Args.hasArg(options::OPT_mno_scatter)) 3108a4dda33SDimitry Andric Features.push_back("+prefer-no-scatter"); 311*0fca6ea1SDimitry Andric if (Args.hasArg(options::OPT_mapx_inline_asm_use_gpr32)) 312*0fca6ea1SDimitry Andric Features.push_back("+inline-asm-use-gpr32"); 313*0fca6ea1SDimitry Andric 314*0fca6ea1SDimitry Andric // Warn for removed 3dnow support 315*0fca6ea1SDimitry Andric if (const Arg *A = 316*0fca6ea1SDimitry Andric Args.getLastArg(options::OPT_m3dnowa, options::OPT_mno_3dnowa, 317*0fca6ea1SDimitry Andric options::OPT_mno_3dnow)) { 318*0fca6ea1SDimitry Andric if (A->getOption().matches(options::OPT_m3dnowa)) 319*0fca6ea1SDimitry Andric D.Diag(diag::warn_drv_clang_unsupported) << A->getAsString(Args); 320*0fca6ea1SDimitry Andric } 321*0fca6ea1SDimitry Andric if (const Arg *A = 322*0fca6ea1SDimitry Andric Args.getLastArg(options::OPT_m3dnow, options::OPT_mno_3dnow)) { 323*0fca6ea1SDimitry Andric if (A->getOption().matches(options::OPT_m3dnow)) 324*0fca6ea1SDimitry Andric D.Diag(diag::warn_drv_clang_unsupported) << A->getAsString(Args); 325*0fca6ea1SDimitry Andric } 3260b57cec5SDimitry Andric } 327