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 llvm::StringMap<bool> HostFeatures; 135 if (llvm::sys::getHostCPUFeatures(HostFeatures)) 136 for (auto &F : HostFeatures) 137 Features.push_back( 138 Args.MakeArgString((F.second ? "+" : "-") + F.first())); 139 } 140 } 141 142 if (Triple.getArchName() == "x86_64h") { 143 // x86_64h implies quite a few of the more modern subtarget features 144 // for Haswell class CPUs, but not all of them. Opt-out of a few. 145 Features.push_back("-rdrnd"); 146 Features.push_back("-aes"); 147 Features.push_back("-pclmul"); 148 Features.push_back("-rtm"); 149 Features.push_back("-fsgsbase"); 150 } 151 152 const llvm::Triple::ArchType ArchType = Triple.getArch(); 153 // Add features to be compatible with gcc for Android. 154 if (Triple.isAndroid()) { 155 if (ArchType == llvm::Triple::x86_64) { 156 Features.push_back("+sse4.2"); 157 Features.push_back("+popcnt"); 158 Features.push_back("+cx16"); 159 } else 160 Features.push_back("+ssse3"); 161 } 162 163 // Translate the high level `-mretpoline` flag to the specific target feature 164 // flags. We also detect if the user asked for retpoline external thunks but 165 // failed to ask for retpolines themselves (through any of the different 166 // flags). This is a bit hacky but keeps existing usages working. We should 167 // consider deprecating this and instead warn if the user requests external 168 // retpoline thunks and *doesn't* request some form of retpolines. 169 auto SpectreOpt = clang::driver::options::ID::OPT_INVALID; 170 if (Args.hasArgNoClaim(options::OPT_mretpoline, options::OPT_mno_retpoline, 171 options::OPT_mspeculative_load_hardening, 172 options::OPT_mno_speculative_load_hardening)) { 173 if (Args.hasFlag(options::OPT_mretpoline, options::OPT_mno_retpoline, 174 false)) { 175 Features.push_back("+retpoline-indirect-calls"); 176 Features.push_back("+retpoline-indirect-branches"); 177 SpectreOpt = options::OPT_mretpoline; 178 } else if (Args.hasFlag(options::OPT_mspeculative_load_hardening, 179 options::OPT_mno_speculative_load_hardening, 180 false)) { 181 // On x86, speculative load hardening relies on at least using retpolines 182 // for indirect calls. 183 Features.push_back("+retpoline-indirect-calls"); 184 SpectreOpt = options::OPT_mspeculative_load_hardening; 185 } 186 } else if (Args.hasFlag(options::OPT_mretpoline_external_thunk, 187 options::OPT_mno_retpoline_external_thunk, false)) { 188 // FIXME: Add a warning about failing to specify `-mretpoline` and 189 // eventually switch to an error here. 190 Features.push_back("+retpoline-indirect-calls"); 191 Features.push_back("+retpoline-indirect-branches"); 192 SpectreOpt = options::OPT_mretpoline_external_thunk; 193 } 194 195 auto LVIOpt = clang::driver::options::ID::OPT_INVALID; 196 if (Args.hasFlag(options::OPT_mlvi_hardening, options::OPT_mno_lvi_hardening, 197 false)) { 198 Features.push_back("+lvi-load-hardening"); 199 Features.push_back("+lvi-cfi"); // load hardening implies CFI protection 200 LVIOpt = options::OPT_mlvi_hardening; 201 } else if (Args.hasFlag(options::OPT_mlvi_cfi, options::OPT_mno_lvi_cfi, 202 false)) { 203 Features.push_back("+lvi-cfi"); 204 LVIOpt = options::OPT_mlvi_cfi; 205 } 206 207 if (Args.hasFlag(options::OPT_m_seses, options::OPT_mno_seses, false)) { 208 if (LVIOpt == options::OPT_mlvi_hardening) 209 D.Diag(diag::err_drv_argument_not_allowed_with) 210 << D.getOpts().getOptionName(options::OPT_mlvi_hardening) 211 << D.getOpts().getOptionName(options::OPT_m_seses); 212 213 if (SpectreOpt != clang::driver::options::ID::OPT_INVALID) 214 D.Diag(diag::err_drv_argument_not_allowed_with) 215 << D.getOpts().getOptionName(SpectreOpt) 216 << D.getOpts().getOptionName(options::OPT_m_seses); 217 218 Features.push_back("+seses"); 219 if (!Args.hasArg(options::OPT_mno_lvi_cfi)) { 220 Features.push_back("+lvi-cfi"); 221 LVIOpt = options::OPT_mlvi_cfi; 222 } 223 } 224 225 if (SpectreOpt != clang::driver::options::ID::OPT_INVALID && 226 LVIOpt != clang::driver::options::ID::OPT_INVALID) { 227 D.Diag(diag::err_drv_argument_not_allowed_with) 228 << D.getOpts().getOptionName(SpectreOpt) 229 << D.getOpts().getOptionName(LVIOpt); 230 } 231 232 for (const Arg *A : Args.filtered(options::OPT_m_x86_AVX10_Features_Group)) { 233 StringRef Name = A->getOption().getName(); 234 A->claim(); 235 236 // Skip over "-m". 237 assert(Name.starts_with("m") && "Invalid feature name."); 238 Name = Name.substr(1); 239 240 bool IsNegative = Name.consume_front("no-"); 241 242 #ifndef NDEBUG 243 assert(Name.starts_with("avx10.") && "Invalid AVX10 feature name."); 244 StringRef Version, Width; 245 std::tie(Version, Width) = Name.substr(6).split('-'); 246 assert(Version == "1" && "Invalid AVX10 feature name."); 247 assert((Width == "256" || Width == "512") && "Invalid AVX10 feature name."); 248 #endif 249 250 Features.push_back(Args.MakeArgString((IsNegative ? "-" : "+") + Name)); 251 } 252 253 // Now add any that the user explicitly requested on the command line, 254 // which may override the defaults. 255 for (const Arg *A : Args.filtered(options::OPT_m_x86_Features_Group, 256 options::OPT_mgeneral_regs_only)) { 257 StringRef Name = A->getOption().getName(); 258 A->claim(); 259 260 // Skip over "-m". 261 assert(Name.starts_with("m") && "Invalid feature name."); 262 Name = Name.substr(1); 263 264 // Replace -mgeneral-regs-only with -x87, -mmx, -sse 265 if (A->getOption().getID() == options::OPT_mgeneral_regs_only) { 266 Features.insert(Features.end(), {"-x87", "-mmx", "-sse"}); 267 continue; 268 } 269 270 bool IsNegative = Name.starts_with("no-"); 271 if (A->getOption().matches(options::OPT_mapx_features_EQ) || 272 A->getOption().matches(options::OPT_mno_apx_features_EQ)) { 273 274 for (StringRef Value : A->getValues()) { 275 if (Value == "egpr" || Value == "push2pop2" || Value == "ppx" || 276 Value == "ndd" || Value == "ccmp" || Value == "cf") { 277 Features.push_back( 278 Args.MakeArgString((IsNegative ? "-" : "+") + Value)); 279 continue; 280 } 281 D.Diag(clang::diag::err_drv_unsupported_option_argument) 282 << A->getSpelling() << Value; 283 } 284 continue; 285 } 286 if (IsNegative) 287 Name = Name.substr(3); 288 Features.push_back(Args.MakeArgString((IsNegative ? "-" : "+") + Name)); 289 } 290 291 // Enable/disable straight line speculation hardening. 292 if (Arg *A = Args.getLastArg(options::OPT_mharden_sls_EQ)) { 293 StringRef Scope = A->getValue(); 294 if (Scope == "all") { 295 Features.push_back("+harden-sls-ijmp"); 296 Features.push_back("+harden-sls-ret"); 297 } else if (Scope == "return") { 298 Features.push_back("+harden-sls-ret"); 299 } else if (Scope == "indirect-jmp") { 300 Features.push_back("+harden-sls-ijmp"); 301 } else if (Scope != "none") { 302 D.Diag(diag::err_drv_unsupported_option_argument) 303 << A->getSpelling() << Scope; 304 } 305 } 306 307 // -mno-gather, -mno-scatter support 308 if (Args.hasArg(options::OPT_mno_gather)) 309 Features.push_back("+prefer-no-gather"); 310 if (Args.hasArg(options::OPT_mno_scatter)) 311 Features.push_back("+prefer-no-scatter"); 312 } 313