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/Option/ArgList.h" 17 #include "llvm/TargetParser/Host.h" 18 19 using namespace clang::driver; 20 using namespace clang::driver::tools; 21 using namespace clang; 22 using namespace llvm::opt; 23 24 std::string x86::getX86TargetCPU(const Driver &D, const ArgList &Args, 25 const llvm::Triple &Triple) { 26 if (const Arg *A = Args.getLastArg(clang::driver::options::OPT_march_EQ)) { 27 StringRef CPU = A->getValue(); 28 if (CPU != "native") 29 return std::string(CPU); 30 31 // FIXME: Reject attempts to use -march=native unless the target matches 32 // the host. 33 CPU = llvm::sys::getHostCPUName(); 34 if (!CPU.empty() && CPU != "generic") 35 return std::string(CPU); 36 } 37 38 if (const Arg *A = Args.getLastArg(options::OPT__SLASH_arch)) { 39 // Mapping built by looking at lib/Basic's X86TargetInfo::initFeatureMap(). 40 // The keys are case-sensitive; this matches link.exe. 41 // 32-bit and 64-bit /arch: flags. 42 llvm::StringMap<StringRef> ArchMap({ 43 {"AVX", "sandybridge"}, 44 {"AVX2", "haswell"}, 45 {"AVX512F", "knl"}, 46 {"AVX512", "skylake-avx512"}, 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 gcc 98 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 119 void 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" or 123 // "ms_abi" as default function attributes. 124 if (const Arg *A = Args.getLastArg(clang::driver::options::OPT_mabi_EQ)) { 125 StringRef DefaultAbi = Triple.isOSWindows() ? "ms" : "sysv"; 126 if (A->getValue() != DefaultAbi) 127 D.Diag(diag::err_drv_unsupported_opt_for_target) 128 << A->getSpelling() << Triple.getTriple(); 129 } 130 131 // If -march=native, autodetect the feature list. 132 if (const Arg *A = Args.getLastArg(clang::driver::options::OPT_march_EQ)) { 133 if (StringRef(A->getValue()) == "native") { 134 for (auto &F : llvm::sys::getHostCPUFeatures()) 135 Features.push_back( 136 Args.MakeArgString((F.second ? "+" : "-") + F.first())); 137 } 138 } 139 140 if (Triple.getArchName() == "x86_64h") { 141 // x86_64h implies quite a few of the more modern subtarget features 142 // for Haswell class CPUs, but not all of them. Opt-out of a few. 143 Features.push_back("-rdrnd"); 144 Features.push_back("-aes"); 145 Features.push_back("-pclmul"); 146 Features.push_back("-rtm"); 147 Features.push_back("-fsgsbase"); 148 } 149 150 const llvm::Triple::ArchType ArchType = Triple.getArch(); 151 // Add features to be compatible with gcc for Android. 152 if (Triple.isAndroid()) { 153 if (ArchType == llvm::Triple::x86_64) { 154 Features.push_back("+sse4.2"); 155 Features.push_back("+popcnt"); 156 Features.push_back("+cx16"); 157 } else 158 Features.push_back("+ssse3"); 159 } 160 161 // Translate the high level `-mretpoline` flag to the specific target feature 162 // flags. We also detect if the user asked for retpoline external thunks but 163 // failed to ask for retpolines themselves (through any of the different 164 // flags). This is a bit hacky but keeps existing usages working. We should 165 // consider deprecating this and instead warn if the user requests external 166 // retpoline thunks and *doesn't* request some form of retpolines. 167 auto SpectreOpt = clang::driver::options::ID::OPT_INVALID; 168 if (Args.hasArgNoClaim(options::OPT_mretpoline, options::OPT_mno_retpoline, 169 options::OPT_mspeculative_load_hardening, 170 options::OPT_mno_speculative_load_hardening)) { 171 if (Args.hasFlag(options::OPT_mretpoline, options::OPT_mno_retpoline, 172 false)) { 173 Features.push_back("+retpoline-indirect-calls"); 174 Features.push_back("+retpoline-indirect-branches"); 175 SpectreOpt = options::OPT_mretpoline; 176 } else if (Args.hasFlag(options::OPT_mspeculative_load_hardening, 177 options::OPT_mno_speculative_load_hardening, 178 false)) { 179 // On x86, speculative load hardening relies on at least using retpolines 180 // for indirect calls. 181 Features.push_back("+retpoline-indirect-calls"); 182 SpectreOpt = options::OPT_mspeculative_load_hardening; 183 } 184 } else if (Args.hasFlag(options::OPT_mretpoline_external_thunk, 185 options::OPT_mno_retpoline_external_thunk, false)) { 186 // FIXME: Add a warning about failing to specify `-mretpoline` and 187 // eventually switch to an error here. 188 Features.push_back("+retpoline-indirect-calls"); 189 Features.push_back("+retpoline-indirect-branches"); 190 SpectreOpt = options::OPT_mretpoline_external_thunk; 191 } 192 193 auto LVIOpt = clang::driver::options::ID::OPT_INVALID; 194 if (Args.hasFlag(options::OPT_mlvi_hardening, options::OPT_mno_lvi_hardening, 195 false)) { 196 Features.push_back("+lvi-load-hardening"); 197 Features.push_back("+lvi-cfi"); // load hardening implies CFI protection 198 LVIOpt = options::OPT_mlvi_hardening; 199 } else if (Args.hasFlag(options::OPT_mlvi_cfi, options::OPT_mno_lvi_cfi, 200 false)) { 201 Features.push_back("+lvi-cfi"); 202 LVIOpt = options::OPT_mlvi_cfi; 203 } 204 205 if (Args.hasFlag(options::OPT_m_seses, options::OPT_mno_seses, false)) { 206 if (LVIOpt == options::OPT_mlvi_hardening) 207 D.Diag(diag::err_drv_argument_not_allowed_with) 208 << D.getOpts().getOptionName(options::OPT_mlvi_hardening) 209 << D.getOpts().getOptionName(options::OPT_m_seses); 210 211 if (SpectreOpt != clang::driver::options::ID::OPT_INVALID) 212 D.Diag(diag::err_drv_argument_not_allowed_with) 213 << D.getOpts().getOptionName(SpectreOpt) 214 << D.getOpts().getOptionName(options::OPT_m_seses); 215 216 Features.push_back("+seses"); 217 if (!Args.hasArg(options::OPT_mno_lvi_cfi)) { 218 Features.push_back("+lvi-cfi"); 219 LVIOpt = options::OPT_mlvi_cfi; 220 } 221 } 222 223 if (SpectreOpt != clang::driver::options::ID::OPT_INVALID && 224 LVIOpt != clang::driver::options::ID::OPT_INVALID) { 225 D.Diag(diag::err_drv_argument_not_allowed_with) 226 << D.getOpts().getOptionName(SpectreOpt) 227 << D.getOpts().getOptionName(LVIOpt); 228 } 229 230 for (const Arg *A : Args.filtered(options::OPT_m_x86_AVX10_Features_Group)) { 231 StringRef Name = A->getOption().getName(); 232 A->claim(); 233 234 // Skip over "-m". 235 assert(Name.starts_with("m") && "Invalid feature name."); 236 Name = Name.substr(1); 237 238 bool IsNegative = Name.consume_front("no-"); 239 240 #ifndef NDEBUG 241 assert(Name.starts_with("avx10.") && "Invalid AVX10 feature name."); 242 StringRef Version, Width; 243 std::tie(Version, Width) = Name.substr(6).split('-'); 244 assert(Version == "1" && "Invalid AVX10 feature name."); 245 assert((Width == "256" || Width == "512") && "Invalid AVX10 feature name."); 246 #endif 247 248 Features.push_back(Args.MakeArgString((IsNegative ? "-" : "+") + Name)); 249 } 250 251 // Now add any that the user explicitly requested on the command line, 252 // which may override the defaults. 253 for (const Arg *A : Args.filtered(options::OPT_m_x86_Features_Group, 254 options::OPT_mgeneral_regs_only)) { 255 StringRef Name = A->getOption().getName(); 256 A->claim(); 257 258 // Skip over "-m". 259 assert(Name.starts_with("m") && "Invalid feature name."); 260 Name = Name.substr(1); 261 262 // Replace -mgeneral-regs-only with -x87, -mmx, -sse 263 if (A->getOption().getID() == options::OPT_mgeneral_regs_only) { 264 Features.insert(Features.end(), {"-x87", "-mmx", "-sse"}); 265 continue; 266 } 267 268 bool IsNegative = Name.starts_with("no-"); 269 if (A->getOption().matches(options::OPT_mapx_features_EQ) || 270 A->getOption().matches(options::OPT_mno_apx_features_EQ)) { 271 272 for (StringRef Value : A->getValues()) { 273 if (Value == "egpr" || Value == "push2pop2" || Value == "ppx" || 274 Value == "ndd" || Value == "ccmp" || Value == "nf" || 275 Value == "cf" || Value == "zu") { 276 Features.push_back( 277 Args.MakeArgString((IsNegative ? "-" : "+") + Value)); 278 continue; 279 } 280 D.Diag(clang::diag::err_drv_unsupported_option_argument) 281 << A->getSpelling() << Value; 282 } 283 continue; 284 } 285 if (IsNegative) 286 Name = Name.substr(3); 287 Features.push_back(Args.MakeArgString((IsNegative ? "-" : "+") + Name)); 288 } 289 290 // Enable/disable straight line speculation hardening. 291 if (Arg *A = Args.getLastArg(options::OPT_mharden_sls_EQ)) { 292 StringRef Scope = A->getValue(); 293 if (Scope == "all") { 294 Features.push_back("+harden-sls-ijmp"); 295 Features.push_back("+harden-sls-ret"); 296 } else if (Scope == "return") { 297 Features.push_back("+harden-sls-ret"); 298 } else if (Scope == "indirect-jmp") { 299 Features.push_back("+harden-sls-ijmp"); 300 } else if (Scope != "none") { 301 D.Diag(diag::err_drv_unsupported_option_argument) 302 << A->getSpelling() << Scope; 303 } 304 } 305 306 // -mno-gather, -mno-scatter support 307 if (Args.hasArg(options::OPT_mno_gather)) 308 Features.push_back("+prefer-no-gather"); 309 if (Args.hasArg(options::OPT_mno_scatter)) 310 Features.push_back("+prefer-no-scatter"); 311 if (Args.hasArg(options::OPT_mapx_inline_asm_use_gpr32)) 312 Features.push_back("+inline-asm-use-gpr32"); 313 314 // Warn for removed 3dnow support 315 if (const Arg *A = 316 Args.getLastArg(options::OPT_m3dnowa, options::OPT_mno_3dnowa, 317 options::OPT_mno_3dnow)) { 318 if (A->getOption().matches(options::OPT_m3dnowa)) 319 D.Diag(diag::warn_drv_clang_unsupported) << A->getAsString(Args); 320 } 321 if (const Arg *A = 322 Args.getLastArg(options::OPT_m3dnow, options::OPT_mno_3dnow)) { 323 if (A->getOption().matches(options::OPT_m3dnow)) 324 D.Diag(diag::warn_drv_clang_unsupported) << A->getAsString(Args); 325 } 326 } 327