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-exception 6 // 7 //===----------------------------------------------------------------------===// 8 9 #include "X86.h" 10 #include "ToolChains/CommonArgs.h" 11 #include "clang/Driver/Driver.h" 12 #include "clang/Driver/DriverDiagnostic.h" 13 #include "clang/Driver/Options.h" 14 #include "llvm/ADT/StringExtras.h" 15 #include "llvm/ADT/StringMap.h" 16 #include "llvm/ADT/StringSwitch.h" 17 #include "llvm/Option/ArgList.h" 18 #include "llvm/Support/Host.h" 19 20 using namespace clang::driver; 21 using namespace clang::driver::tools; 22 using namespace clang; 23 using namespace llvm::opt; 24 25 std::string x86::getX86TargetCPU(const Driver &D, const ArgList &Args, 26 const llvm::Triple &Triple) { 27 if (const Arg *A = Args.getLastArg(clang::driver::options::OPT_march_EQ)) { 28 StringRef CPU = A->getValue(); 29 if (CPU != "native") 30 return std::string(CPU); 31 32 // FIXME: Reject attempts to use -march=native unless the target matches 33 // the host. 34 // 35 // FIXME: We should also incorporate the detected target features for use 36 // with -native. 37 CPU = llvm::sys::getHostCPUName(); 38 if (!CPU.empty() && CPU != "generic") 39 return std::string(CPU); 40 } 41 42 if (const Arg *A = Args.getLastArg(options::OPT__SLASH_arch)) { 43 // Mapping built by looking at lib/Basic's X86TargetInfo::initFeatureMap(). 44 // The keys are case-sensitive; this matches link.exe. 45 // 32-bit and 64-bit /arch: flags. 46 llvm::StringMap<StringRef> ArchMap({ 47 {"AVX", "sandybridge"}, 48 {"AVX2", "haswell"}, 49 {"AVX512F", "knl"}, 50 {"AVX512", "skylake-avx512"}, 51 }); 52 if (Triple.getArch() == llvm::Triple::x86) { 53 // 32-bit-only /arch: flags. 54 ArchMap.insert({ 55 {"IA32", "i386"}, 56 {"SSE", "pentium3"}, 57 {"SSE2", "pentium4"}, 58 }); 59 } 60 StringRef CPU = ArchMap.lookup(A->getValue()); 61 if (CPU.empty()) { 62 std::vector<StringRef> ValidArchs{ArchMap.keys().begin(), 63 ArchMap.keys().end()}; 64 sort(ValidArchs); 65 D.Diag(diag::warn_drv_invalid_arch_name_with_suggestion) 66 << A->getValue() << (Triple.getArch() == llvm::Triple::x86) 67 << join(ValidArchs, ", "); 68 } 69 return std::string(CPU); 70 } 71 72 // Select the default CPU if none was given (or detection failed). 73 74 if (!Triple.isX86()) 75 return ""; // This routine is only handling x86 targets. 76 77 bool Is64Bit = Triple.getArch() == llvm::Triple::x86_64; 78 79 // FIXME: Need target hooks. 80 if (Triple.isOSDarwin()) { 81 if (Triple.getArchName() == "x86_64h") 82 return "core-avx2"; 83 // macosx10.12 drops support for all pre-Penryn Macs. 84 // Simulators can still run on 10.11 though, like Xcode. 85 if (Triple.isMacOSX() && !Triple.isOSVersionLT(10, 12)) 86 return "penryn"; 87 88 if (Triple.isDriverKit()) 89 return "nehalem"; 90 91 // The oldest x86_64 Macs have core2/Merom; the oldest x86 Macs have Yonah. 92 return Is64Bit ? "core2" : "yonah"; 93 } 94 95 // Set up default CPU name for PS4/PS5 compilers. 96 if (Triple.isPS4()) 97 return "btver2"; 98 if (Triple.isPS5()) 99 return "znver2"; 100 101 // On Android use targets compatible with gcc 102 if (Triple.isAndroid()) 103 return Is64Bit ? "x86-64" : "i686"; 104 105 // Everything else goes to x86-64 in 64-bit mode. 106 if (Is64Bit) 107 return "x86-64"; 108 109 switch (Triple.getOS()) { 110 case llvm::Triple::NetBSD: 111 return "i486"; 112 case llvm::Triple::Haiku: 113 case llvm::Triple::OpenBSD: 114 return "i586"; 115 case llvm::Triple::FreeBSD: 116 return "i686"; 117 default: 118 // Fallback to p4. 119 return "pentium4"; 120 } 121 } 122 123 void x86::getX86TargetFeatures(const Driver &D, const llvm::Triple &Triple, 124 const ArgList &Args, 125 std::vector<StringRef> &Features) { 126 // If -march=native, autodetect the feature list. 127 if (const Arg *A = Args.getLastArg(clang::driver::options::OPT_march_EQ)) { 128 if (StringRef(A->getValue()) == "native") { 129 llvm::StringMap<bool> HostFeatures; 130 if (llvm::sys::getHostCPUFeatures(HostFeatures)) 131 for (auto &F : HostFeatures) 132 Features.push_back( 133 Args.MakeArgString((F.second ? "+" : "-") + F.first())); 134 } 135 } 136 137 if (Triple.getArchName() == "x86_64h") { 138 // x86_64h implies quite a few of the more modern subtarget features 139 // for Haswell class CPUs, but not all of them. Opt-out of a few. 140 Features.push_back("-rdrnd"); 141 Features.push_back("-aes"); 142 Features.push_back("-pclmul"); 143 Features.push_back("-rtm"); 144 Features.push_back("-fsgsbase"); 145 } 146 147 const llvm::Triple::ArchType ArchType = Triple.getArch(); 148 // Add features to be compatible with gcc for Android. 149 if (Triple.isAndroid()) { 150 if (ArchType == llvm::Triple::x86_64) { 151 Features.push_back("+sse4.2"); 152 Features.push_back("+popcnt"); 153 Features.push_back("+cx16"); 154 } else 155 Features.push_back("+ssse3"); 156 } 157 158 // Translate the high level `-mretpoline` flag to the specific target feature 159 // flags. We also detect if the user asked for retpoline external thunks but 160 // failed to ask for retpolines themselves (through any of the different 161 // flags). This is a bit hacky but keeps existing usages working. We should 162 // consider deprecating this and instead warn if the user requests external 163 // retpoline thunks and *doesn't* request some form of retpolines. 164 auto SpectreOpt = clang::driver::options::ID::OPT_INVALID; 165 if (Args.hasArgNoClaim(options::OPT_mretpoline, options::OPT_mno_retpoline, 166 options::OPT_mspeculative_load_hardening, 167 options::OPT_mno_speculative_load_hardening)) { 168 if (Args.hasFlag(options::OPT_mretpoline, options::OPT_mno_retpoline, 169 false)) { 170 Features.push_back("+retpoline-indirect-calls"); 171 Features.push_back("+retpoline-indirect-branches"); 172 SpectreOpt = options::OPT_mretpoline; 173 } else if (Args.hasFlag(options::OPT_mspeculative_load_hardening, 174 options::OPT_mno_speculative_load_hardening, 175 false)) { 176 // On x86, speculative load hardening relies on at least using retpolines 177 // for indirect calls. 178 Features.push_back("+retpoline-indirect-calls"); 179 SpectreOpt = options::OPT_mspeculative_load_hardening; 180 } 181 } else if (Args.hasFlag(options::OPT_mretpoline_external_thunk, 182 options::OPT_mno_retpoline_external_thunk, false)) { 183 // FIXME: Add a warning about failing to specify `-mretpoline` and 184 // eventually switch to an error here. 185 Features.push_back("+retpoline-indirect-calls"); 186 Features.push_back("+retpoline-indirect-branches"); 187 SpectreOpt = options::OPT_mretpoline_external_thunk; 188 } 189 190 auto LVIOpt = clang::driver::options::ID::OPT_INVALID; 191 if (Args.hasFlag(options::OPT_mlvi_hardening, options::OPT_mno_lvi_hardening, 192 false)) { 193 Features.push_back("+lvi-load-hardening"); 194 Features.push_back("+lvi-cfi"); // load hardening implies CFI protection 195 LVIOpt = options::OPT_mlvi_hardening; 196 } else if (Args.hasFlag(options::OPT_mlvi_cfi, options::OPT_mno_lvi_cfi, 197 false)) { 198 Features.push_back("+lvi-cfi"); 199 LVIOpt = options::OPT_mlvi_cfi; 200 } 201 202 if (Args.hasFlag(options::OPT_m_seses, options::OPT_mno_seses, false)) { 203 if (LVIOpt == options::OPT_mlvi_hardening) 204 D.Diag(diag::err_drv_argument_not_allowed_with) 205 << D.getOpts().getOptionName(options::OPT_mlvi_hardening) 206 << D.getOpts().getOptionName(options::OPT_m_seses); 207 208 if (SpectreOpt != clang::driver::options::ID::OPT_INVALID) 209 D.Diag(diag::err_drv_argument_not_allowed_with) 210 << D.getOpts().getOptionName(SpectreOpt) 211 << D.getOpts().getOptionName(options::OPT_m_seses); 212 213 Features.push_back("+seses"); 214 if (!Args.hasArg(options::OPT_mno_lvi_cfi)) { 215 Features.push_back("+lvi-cfi"); 216 LVIOpt = options::OPT_mlvi_cfi; 217 } 218 } 219 220 if (SpectreOpt != clang::driver::options::ID::OPT_INVALID && 221 LVIOpt != clang::driver::options::ID::OPT_INVALID) { 222 D.Diag(diag::err_drv_argument_not_allowed_with) 223 << D.getOpts().getOptionName(SpectreOpt) 224 << D.getOpts().getOptionName(LVIOpt); 225 } 226 227 // Now add any that the user explicitly requested on the command line, 228 // which may override the defaults. 229 for (const Arg *A : Args.filtered(options::OPT_m_x86_Features_Group, 230 options::OPT_mgeneral_regs_only)) { 231 StringRef Name = A->getOption().getName(); 232 A->claim(); 233 234 // Skip over "-m". 235 assert(Name.startswith("m") && "Invalid feature name."); 236 Name = Name.substr(1); 237 238 // Replace -mgeneral-regs-only with -x87, -mmx, -sse 239 if (A->getOption().getID() == options::OPT_mgeneral_regs_only) { 240 Features.insert(Features.end(), {"-x87", "-mmx", "-sse"}); 241 continue; 242 } 243 244 bool IsNegative = Name.startswith("no-"); 245 if (IsNegative) 246 Name = Name.substr(3); 247 Features.push_back(Args.MakeArgString((IsNegative ? "-" : "+") + Name)); 248 } 249 250 // Enable/disable straight line speculation hardening. 251 if (Arg *A = Args.getLastArg(options::OPT_mharden_sls_EQ)) { 252 StringRef Scope = A->getValue(); 253 if (Scope == "all") { 254 Features.push_back("+harden-sls-ijmp"); 255 Features.push_back("+harden-sls-ret"); 256 } else if (Scope == "return") { 257 Features.push_back("+harden-sls-ret"); 258 } else if (Scope == "indirect-jmp") { 259 Features.push_back("+harden-sls-ijmp"); 260 } else if (Scope != "none") { 261 D.Diag(diag::err_drv_unsupported_option_argument) 262 << A->getOption().getName() << Scope; 263 } 264 } 265 } 266