| 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 "clang/Driver/Driver.h" |
| 11 | #include "clang/Options/Options.h" |
| 12 | #include "llvm/ADT/STLExtras.h" |
| 13 | #include "llvm/ADT/StringExtras.h" |
| 14 | #include "llvm/ADT/StringMap.h" |
| 15 | #include "llvm/Option/ArgList.h" |
| 16 | #include "llvm/TargetParser/Host.h" |
| 17 | #include "llvm/TargetParser/X86TargetParser.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(Ids: 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(Ids: 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 | {"AVX10.1" , "sapphirerapids" }, |
| 48 | {"AVX10.2" , "diamondrapids" }, |
| 49 | }); |
| 50 | if (Triple.getArch() == llvm::Triple::x86) { |
| 51 | // 32-bit-only /arch: flags. |
| 52 | ArchMap.insert(List: { |
| 53 | {"IA32" , "i386" }, |
| 54 | {"SSE" , "pentium3" }, |
| 55 | {"SSE2" , "pentium4" }, |
| 56 | }); |
| 57 | } |
| 58 | StringRef CPU = ArchMap.lookup(Key: A->getValue()); |
| 59 | if (CPU.empty()) { |
| 60 | std::vector<StringRef> ValidArchs{ArchMap.keys().begin(), |
| 61 | ArchMap.keys().end()}; |
| 62 | sort(C&: ValidArchs); |
| 63 | D.Diag(DiagID: diag::warn_drv_invalid_arch_name_with_suggestion) |
| 64 | << A->getValue() << (Triple.getArch() == llvm::Triple::x86) |
| 65 | << join(R&: ValidArchs, Separator: ", " ); |
| 66 | } |
| 67 | return std::string(CPU); |
| 68 | } |
| 69 | |
| 70 | // Select the default CPU if none was given (or detection failed). |
| 71 | |
| 72 | if (!Triple.isX86()) |
| 73 | return "" ; // This routine is only handling x86 targets. |
| 74 | |
| 75 | bool Is64Bit = Triple.getArch() == llvm::Triple::x86_64; |
| 76 | |
| 77 | // FIXME: Need target hooks. |
| 78 | if (Triple.isOSDarwin()) { |
| 79 | if (Triple.getArchName() == "x86_64h" ) |
| 80 | return "core-avx2" ; |
| 81 | // macosx10.12 drops support for all pre-Penryn Macs. |
| 82 | // Simulators can still run on 10.11 though, like Xcode. |
| 83 | if (Triple.isMacOSX() && !Triple.isOSVersionLT(Major: 10, Minor: 12)) |
| 84 | return "penryn" ; |
| 85 | |
| 86 | if (Triple.isDriverKit()) |
| 87 | return "nehalem" ; |
| 88 | |
| 89 | // The oldest x86_64 Macs have core2/Merom; the oldest x86 Macs have Yonah. |
| 90 | return Is64Bit ? "core2" : "yonah" ; |
| 91 | } |
| 92 | |
| 93 | // Set up default CPU name for PS4/PS5 compilers. |
| 94 | if (Triple.isPS4()) |
| 95 | return "btver2" ; |
| 96 | if (Triple.isPS5()) |
| 97 | return "znver2" ; |
| 98 | |
| 99 | // On Android use targets compatible with gcc |
| 100 | if (Triple.isAndroid()) |
| 101 | return Is64Bit ? "x86-64" : "i686" ; |
| 102 | |
| 103 | // Everything else goes to x86-64 in 64-bit mode. |
| 104 | if (Is64Bit) |
| 105 | return "x86-64" ; |
| 106 | |
| 107 | switch (Triple.getOS()) { |
| 108 | case llvm::Triple::NetBSD: |
| 109 | return "i486" ; |
| 110 | case llvm::Triple::Haiku: |
| 111 | case llvm::Triple::OpenBSD: |
| 112 | return "i586" ; |
| 113 | case llvm::Triple::FreeBSD: |
| 114 | return "i686" ; |
| 115 | default: |
| 116 | // Fallback to p4. |
| 117 | return "pentium4" ; |
| 118 | } |
| 119 | } |
| 120 | |
| 121 | void x86::getX86TargetFeatures(const Driver &D, const llvm::Triple &Triple, |
| 122 | const ArgList &Args, |
| 123 | std::vector<StringRef> &Features) { |
| 124 | // Claim and report unsupported -mabi=. Note: we don't support "sysv_abi" or |
| 125 | // "ms_abi" as default function attributes. |
| 126 | if (const Arg *A = Args.getLastArg(Ids: options::OPT_mabi_EQ)) { |
| 127 | StringRef DefaultAbi = |
| 128 | (Triple.isOSWindows() || Triple.isUEFI()) ? "ms" : "sysv" ; |
| 129 | if (A->getValue() != DefaultAbi) |
| 130 | D.Diag(DiagID: diag::err_drv_unsupported_opt_for_target) |
| 131 | << A->getSpelling() << Triple.getTriple(); |
| 132 | } |
| 133 | |
| 134 | // If -march=native, autodetect the feature list. |
| 135 | if (const Arg *A = Args.getLastArg(Ids: options::OPT_march_EQ)) { |
| 136 | if (StringRef(A->getValue()) == "native" ) { |
| 137 | for (auto &F : llvm::sys::getHostCPUFeatures()) |
| 138 | Features.push_back( |
| 139 | x: Args.MakeArgString(Str: (F.second ? "+" : "-" ) + F.first())); |
| 140 | } |
| 141 | } |
| 142 | |
| 143 | if (Triple.getArchName() == "x86_64h" ) { |
| 144 | // x86_64h implies quite a few of the more modern subtarget features |
| 145 | // for Haswell class CPUs, but not all of them. Opt-out of a few. |
| 146 | Features.push_back(x: "-rdrnd" ); |
| 147 | Features.push_back(x: "-aes" ); |
| 148 | Features.push_back(x: "-pclmul" ); |
| 149 | Features.push_back(x: "-rtm" ); |
| 150 | Features.push_back(x: "-fsgsbase" ); |
| 151 | } |
| 152 | |
| 153 | const llvm::Triple::ArchType ArchType = Triple.getArch(); |
| 154 | |
| 155 | // Add features to be compatible with gcc for Android. |
| 156 | if (Triple.isAndroid()) { |
| 157 | if (ArchType == llvm::Triple::x86_64) { |
| 158 | Features.push_back(x: "+sse4.2" ); |
| 159 | Features.push_back(x: "+popcnt" ); |
| 160 | Features.push_back(x: "+cx16" ); |
| 161 | } else |
| 162 | Features.push_back(x: "+ssse3" ); |
| 163 | } |
| 164 | |
| 165 | // Translate the high level `-mretpoline` flag to the specific target feature |
| 166 | // flags. We also detect if the user asked for retpoline external thunks but |
| 167 | // failed to ask for retpolines themselves (through any of the different |
| 168 | // flags). This is a bit hacky but keeps existing usages working. We should |
| 169 | // consider deprecating this and instead warn if the user requests external |
| 170 | // retpoline thunks and *doesn't* request some form of retpolines. |
| 171 | auto SpectreOpt = options::ID::OPT_INVALID; |
| 172 | if (Args.hasArgNoClaim(Ids: options::OPT_mretpoline, Ids: options::OPT_mno_retpoline, |
| 173 | Ids: options::OPT_mspeculative_load_hardening, |
| 174 | Ids: options::OPT_mno_speculative_load_hardening)) { |
| 175 | if (Args.hasFlag(Pos: options::OPT_mretpoline, Neg: options::OPT_mno_retpoline, |
| 176 | Default: false)) { |
| 177 | Features.push_back(x: "+retpoline-indirect-calls" ); |
| 178 | Features.push_back(x: "+retpoline-indirect-branches" ); |
| 179 | SpectreOpt = options::OPT_mretpoline; |
| 180 | } else if (Args.hasFlag(Pos: options::OPT_mspeculative_load_hardening, |
| 181 | Neg: options::OPT_mno_speculative_load_hardening, |
| 182 | Default: false)) { |
| 183 | // On x86, speculative load hardening relies on at least using retpolines |
| 184 | // for indirect calls. |
| 185 | Features.push_back(x: "+retpoline-indirect-calls" ); |
| 186 | SpectreOpt = options::OPT_mspeculative_load_hardening; |
| 187 | } |
| 188 | } else if (Args.hasFlag(Pos: options::OPT_mretpoline_external_thunk, |
| 189 | Neg: options::OPT_mno_retpoline_external_thunk, Default: false)) { |
| 190 | // FIXME: Add a warning about failing to specify `-mretpoline` and |
| 191 | // eventually switch to an error here. |
| 192 | Features.push_back(x: "+retpoline-indirect-calls" ); |
| 193 | Features.push_back(x: "+retpoline-indirect-branches" ); |
| 194 | SpectreOpt = options::OPT_mretpoline_external_thunk; |
| 195 | } |
| 196 | |
| 197 | auto LVIOpt = options::ID::OPT_INVALID; |
| 198 | if (Args.hasFlag(Pos: options::OPT_mlvi_hardening, Neg: options::OPT_mno_lvi_hardening, |
| 199 | Default: false)) { |
| 200 | Features.push_back(x: "+lvi-load-hardening" ); |
| 201 | Features.push_back(x: "+lvi-cfi" ); // load hardening implies CFI protection |
| 202 | LVIOpt = options::OPT_mlvi_hardening; |
| 203 | } else if (Args.hasFlag(Pos: options::OPT_mlvi_cfi, Neg: options::OPT_mno_lvi_cfi, |
| 204 | Default: false)) { |
| 205 | Features.push_back(x: "+lvi-cfi" ); |
| 206 | LVIOpt = options::OPT_mlvi_cfi; |
| 207 | } |
| 208 | |
| 209 | if (Args.hasFlag(Pos: options::OPT_m_seses, Neg: options::OPT_mno_seses, Default: false)) { |
| 210 | if (LVIOpt == options::OPT_mlvi_hardening) |
| 211 | D.Diag(DiagID: diag::err_drv_argument_not_allowed_with) |
| 212 | << D.getOpts().getOptionName(id: options::OPT_mlvi_hardening) |
| 213 | << D.getOpts().getOptionName(id: options::OPT_m_seses); |
| 214 | |
| 215 | if (SpectreOpt != options::ID::OPT_INVALID) |
| 216 | D.Diag(DiagID: diag::err_drv_argument_not_allowed_with) |
| 217 | << D.getOpts().getOptionName(id: SpectreOpt) |
| 218 | << D.getOpts().getOptionName(id: options::OPT_m_seses); |
| 219 | |
| 220 | Features.push_back(x: "+seses" ); |
| 221 | if (!Args.hasArg(Ids: options::OPT_mno_lvi_cfi)) { |
| 222 | Features.push_back(x: "+lvi-cfi" ); |
| 223 | LVIOpt = options::OPT_mlvi_cfi; |
| 224 | } |
| 225 | } |
| 226 | |
| 227 | if (SpectreOpt != options::ID::OPT_INVALID && |
| 228 | LVIOpt != options::ID::OPT_INVALID) { |
| 229 | D.Diag(DiagID: diag::err_drv_argument_not_allowed_with) |
| 230 | << D.getOpts().getOptionName(id: SpectreOpt) |
| 231 | << D.getOpts().getOptionName(id: LVIOpt); |
| 232 | } |
| 233 | |
| 234 | enum class EGPRFeature { Unknown, Disabled, Enabled }; |
| 235 | EGPRFeature EGPROpt = EGPRFeature::Unknown; |
| 236 | // Now add any that the user explicitly requested on the command line, |
| 237 | // which may override the defaults. |
| 238 | for (const Arg *A : Args.filtered(Ids: options::OPT_m_x86_Features_Group, |
| 239 | Ids: options::OPT_mgeneral_regs_only)) { |
| 240 | StringRef Name = A->getOption().getName(); |
| 241 | A->claim(); |
| 242 | |
| 243 | // Skip over "-m". |
| 244 | assert(Name.starts_with("m" ) && "Invalid feature name." ); |
| 245 | Name = Name.substr(Start: 1); |
| 246 | |
| 247 | // Replace -mgeneral-regs-only with -x87, -mmx, -sse |
| 248 | if (A->getOption().getID() == options::OPT_mgeneral_regs_only) { |
| 249 | Features.insert(position: Features.end(), l: {"-x87" , "-mmx" , "-sse" }); |
| 250 | continue; |
| 251 | } |
| 252 | |
| 253 | bool IsNegative = Name.starts_with(Prefix: "no-" ); |
| 254 | |
| 255 | bool Not64Bit = ArchType != llvm::Triple::x86_64; |
| 256 | if (Not64Bit && Name == "uintr" ) |
| 257 | D.Diag(DiagID: diag::err_drv_unsupported_opt_for_target) |
| 258 | << A->getSpelling() << Triple.getTriple(); |
| 259 | |
| 260 | if (IsNegative) |
| 261 | Name = Name.substr(Start: 3); |
| 262 | |
| 263 | if (A->getOption().matches(ID: options::OPT_mapxf) || |
| 264 | A->getOption().matches(ID: options::OPT_mno_apxf)) { |
| 265 | if (IsNegative) { |
| 266 | EGPROpt = EGPRFeature::Disabled; |
| 267 | Features.insert(position: Features.end(), |
| 268 | l: {"-egpr" , "-ndd" , "-ccmp" , "-nf" , "-zu" , "-jmpabs" }); |
| 269 | if (!Triple.isOSWindows()) |
| 270 | Features.insert(position: Features.end(), l: {"-push2pop2" , "-ppx" }); |
| 271 | } else { |
| 272 | EGPROpt = EGPRFeature::Enabled; |
| 273 | Features.insert(position: Features.end(), |
| 274 | l: {"+egpr" , "+ndd" , "+ccmp" , "+nf" , "+zu" , "+jmpabs" }); |
| 275 | if (!Triple.isOSWindows()) |
| 276 | Features.insert(position: Features.end(), l: {"+push2pop2" , "+ppx" }); |
| 277 | |
| 278 | if (Not64Bit) |
| 279 | D.Diag(DiagID: diag::err_drv_unsupported_opt_for_target) |
| 280 | << StringRef("-mapxf" ) << Triple.getTriple(); |
| 281 | } |
| 282 | continue; |
| 283 | } |
| 284 | |
| 285 | if (A->getOption().matches(ID: options::OPT_mapx_features_EQ) || |
| 286 | A->getOption().matches(ID: options::OPT_mno_apx_features_EQ)) { |
| 287 | if (Not64Bit && !IsNegative) |
| 288 | D.Diag(DiagID: diag::err_drv_unsupported_opt_for_target) |
| 289 | << StringRef("-mapx-features=" ) << Triple.getTriple(); |
| 290 | |
| 291 | for (StringRef Value : A->getValues()) { |
| 292 | if (Value != "egpr" && Value != "push2pop2" && Value != "ppx" && |
| 293 | Value != "ndd" && Value != "ccmp" && Value != "nf" && |
| 294 | Value != "cf" && Value != "zu" && Value != "jmpabs" ) |
| 295 | D.Diag(DiagID: clang::diag::err_drv_unsupported_option_argument) |
| 296 | << A->getSpelling() << Value; |
| 297 | |
| 298 | if (Value == "egpr" ) { |
| 299 | EGPROpt = IsNegative ? EGPRFeature::Disabled : EGPRFeature::Enabled; |
| 300 | } |
| 301 | |
| 302 | Features.push_back( |
| 303 | x: Args.MakeArgString(Str: (IsNegative ? "-" : "+" ) + Value)); |
| 304 | } |
| 305 | continue; |
| 306 | } |
| 307 | |
| 308 | if (Name == "egpr" ) { |
| 309 | EGPROpt = IsNegative ? EGPRFeature::Disabled : EGPRFeature::Enabled; |
| 310 | } |
| 311 | Features.push_back(x: Args.MakeArgString(Str: (IsNegative ? "-" : "+" ) + Name)); |
| 312 | } |
| 313 | |
| 314 | // Enable/disable straight line speculation hardening. |
| 315 | if (Arg *A = Args.getLastArg(Ids: options::OPT_mharden_sls_EQ)) { |
| 316 | StringRef Scope = A->getValue(); |
| 317 | if (Scope == "all" ) { |
| 318 | Features.push_back(x: "+harden-sls-ijmp" ); |
| 319 | Features.push_back(x: "+harden-sls-ret" ); |
| 320 | } else if (Scope == "return" ) { |
| 321 | Features.push_back(x: "+harden-sls-ret" ); |
| 322 | } else if (Scope == "indirect-jmp" ) { |
| 323 | Features.push_back(x: "+harden-sls-ijmp" ); |
| 324 | } else if (Scope != "none" ) { |
| 325 | D.Diag(DiagID: diag::err_drv_unsupported_option_argument) |
| 326 | << A->getSpelling() << Scope; |
| 327 | } |
| 328 | } |
| 329 | |
| 330 | // -mno-gather, -mno-scatter support |
| 331 | if (Args.hasArg(Ids: options::OPT_mno_gather)) |
| 332 | Features.push_back(x: "+prefer-no-gather" ); |
| 333 | if (Args.hasArg(Ids: options::OPT_mno_scatter)) |
| 334 | Features.push_back(x: "+prefer-no-scatter" ); |
| 335 | if (Args.hasArg(Ids: options::OPT_mapx_inline_asm_use_gpr32)) |
| 336 | Features.push_back(x: "+inline-asm-use-gpr32" ); |
| 337 | |
| 338 | // Warn for removed 3dnow support |
| 339 | if (const Arg *A = |
| 340 | Args.getLastArg(Ids: options::OPT_m3dnowa, Ids: options::OPT_mno_3dnowa, |
| 341 | Ids: options::OPT_mno_3dnow)) { |
| 342 | if (A->getOption().matches(ID: options::OPT_m3dnowa)) |
| 343 | D.Diag(DiagID: diag::warn_drv_clang_unsupported) << A->getAsString(Args); |
| 344 | } |
| 345 | if (const Arg *A = |
| 346 | Args.getLastArg(Ids: options::OPT_m3dnow, Ids: options::OPT_mno_3dnow)) { |
| 347 | if (A->getOption().matches(ID: options::OPT_m3dnow)) |
| 348 | D.Diag(DiagID: diag::warn_drv_clang_unsupported) << A->getAsString(Args); |
| 349 | } |
| 350 | |
| 351 | // Handle features corresponding to "-ffixed-X" options |
| 352 | #define RESERVE_REG(REG) \ |
| 353 | if (Args.hasArg(options::OPT_ffixed_##REG)) \ |
| 354 | Features.push_back("+reserve-" #REG); |
| 355 | RESERVE_REG(r8) |
| 356 | RESERVE_REG(r9) |
| 357 | RESERVE_REG(r10) |
| 358 | RESERVE_REG(r11) |
| 359 | RESERVE_REG(r12) |
| 360 | RESERVE_REG(r13) |
| 361 | RESERVE_REG(r14) |
| 362 | RESERVE_REG(r15) |
| 363 | #undef RESERVE_REG |
| 364 | |
| 365 | bool NeedDetectEGPR = Args.hasArg( |
| 366 | Ids: options::OPT_ffixed_r16, Ids: options::OPT_ffixed_r17, Ids: options::OPT_ffixed_r18, |
| 367 | Ids: options::OPT_ffixed_r19, Ids: options::OPT_ffixed_r20, Ids: options::OPT_ffixed_r21, |
| 368 | Ids: options::OPT_ffixed_r22, Ids: options::OPT_ffixed_r23, Ids: options::OPT_ffixed_r24, |
| 369 | Ids: options::OPT_ffixed_r25, Ids: options::OPT_ffixed_r26, Ids: options::OPT_ffixed_r27, |
| 370 | Ids: options::OPT_ffixed_r28, Ids: options::OPT_ffixed_r29, Ids: options::OPT_ffixed_r30, |
| 371 | Ids: options::OPT_ffixed_r31); |
| 372 | if (NeedDetectEGPR && EGPROpt == EGPRFeature::Unknown && |
| 373 | ArchType == llvm::Triple::x86_64) { |
| 374 | SmallVector<StringRef, 16> CPUFeatures; |
| 375 | llvm::X86::getFeaturesForCPU(CPU: getX86TargetCPU(D, Args, Triple), Features&: CPUFeatures); |
| 376 | EGPROpt = llvm::is_contained(Range&: CPUFeatures, Element: "+egpr" ) ? EGPRFeature::Enabled |
| 377 | : EGPRFeature::Disabled; |
| 378 | } |
| 379 | #define RESERVE_EGPR(REG) \ |
| 380 | if (Args.hasArg(options::OPT_ffixed_##REG)) { \ |
| 381 | if (EGPROpt != EGPRFeature::Enabled) \ |
| 382 | D.Diag(diag::err_drv_unsupported_opt_for_target) \ |
| 383 | << "-ffixed-" #REG << Triple.getTriple(); \ |
| 384 | else \ |
| 385 | Features.push_back("+reserve-" #REG); \ |
| 386 | } |
| 387 | RESERVE_EGPR(r16) |
| 388 | RESERVE_EGPR(r17) |
| 389 | RESERVE_EGPR(r18) |
| 390 | RESERVE_EGPR(r19) |
| 391 | RESERVE_EGPR(r20) |
| 392 | RESERVE_EGPR(r21) |
| 393 | RESERVE_EGPR(r22) |
| 394 | RESERVE_EGPR(r23) |
| 395 | RESERVE_EGPR(r24) |
| 396 | RESERVE_EGPR(r25) |
| 397 | RESERVE_EGPR(r26) |
| 398 | RESERVE_EGPR(r27) |
| 399 | RESERVE_EGPR(r28) |
| 400 | RESERVE_EGPR(r29) |
| 401 | RESERVE_EGPR(r30) |
| 402 | RESERVE_EGPR(r31) |
| 403 | #undef RESERVE_EGPR |
| 404 | } |
| 405 | |