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/Support/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 // If -march=native, autodetect the feature list. 123 if (const Arg *A = Args.getLastArg(clang::driver::options::OPT_march_EQ)) { 124 if (StringRef(A->getValue()) == "native") { 125 llvm::StringMap<bool> HostFeatures; 126 if (llvm::sys::getHostCPUFeatures(HostFeatures)) 127 for (auto &F : HostFeatures) 128 Features.push_back( 129 Args.MakeArgString((F.second ? "+" : "-") + F.first())); 130 } 131 } 132 133 if (Triple.getArchName() == "x86_64h") { 134 // x86_64h implies quite a few of the more modern subtarget features 135 // for Haswell class CPUs, but not all of them. Opt-out of a few. 136 Features.push_back("-rdrnd"); 137 Features.push_back("-aes"); 138 Features.push_back("-pclmul"); 139 Features.push_back("-rtm"); 140 Features.push_back("-fsgsbase"); 141 } 142 143 const llvm::Triple::ArchType ArchType = Triple.getArch(); 144 // Add features to be compatible with gcc for Android. 145 if (Triple.isAndroid()) { 146 if (ArchType == llvm::Triple::x86_64) { 147 Features.push_back("+sse4.2"); 148 Features.push_back("+popcnt"); 149 Features.push_back("+cx16"); 150 } else 151 Features.push_back("+ssse3"); 152 } 153 154 // Translate the high level `-mretpoline` flag to the specific target feature 155 // flags. We also detect if the user asked for retpoline external thunks but 156 // failed to ask for retpolines themselves (through any of the different 157 // flags). This is a bit hacky but keeps existing usages working. We should 158 // consider deprecating this and instead warn if the user requests external 159 // retpoline thunks and *doesn't* request some form of retpolines. 160 auto SpectreOpt = clang::driver::options::ID::OPT_INVALID; 161 if (Triple.isOSOpenBSD() && Triple.getArch() == llvm::Triple::x86_64 && 162 Args.hasFlag(options::OPT_mretpoline, options::OPT_mno_retpoline, true)) { 163 Features.push_back("+retpoline-indirect-calls"); 164 Features.push_back("+retpoline-indirect-branches"); 165 SpectreOpt = options::OPT_mretpoline; 166 } else 167 if (Args.hasArgNoClaim(options::OPT_mretpoline, options::OPT_mno_retpoline, 168 options::OPT_mspeculative_load_hardening, 169 options::OPT_mno_speculative_load_hardening)) { 170 if (Args.hasFlag(options::OPT_mretpoline, options::OPT_mno_retpoline, 171 false)) { 172 Features.push_back("+retpoline-indirect-calls"); 173 Features.push_back("+retpoline-indirect-branches"); 174 SpectreOpt = options::OPT_mretpoline; 175 } else if (Args.hasFlag(options::OPT_mspeculative_load_hardening, 176 options::OPT_mno_speculative_load_hardening, 177 false)) { 178 // On x86, speculative load hardening relies on at least using retpolines 179 // for indirect calls. 180 Features.push_back("+retpoline-indirect-calls"); 181 SpectreOpt = options::OPT_mspeculative_load_hardening; 182 } 183 } else if (Args.hasFlag(options::OPT_mretpoline_external_thunk, 184 options::OPT_mno_retpoline_external_thunk, false)) { 185 // FIXME: Add a warning about failing to specify `-mretpoline` and 186 // eventually switch to an error here. 187 Features.push_back("+retpoline-indirect-calls"); 188 Features.push_back("+retpoline-indirect-branches"); 189 SpectreOpt = options::OPT_mretpoline_external_thunk; 190 } 191 192 auto LVIOpt = clang::driver::options::ID::OPT_INVALID; 193 if (Args.hasFlag(options::OPT_mlvi_hardening, options::OPT_mno_lvi_hardening, 194 false)) { 195 Features.push_back("+lvi-load-hardening"); 196 Features.push_back("+lvi-cfi"); // load hardening implies CFI protection 197 LVIOpt = options::OPT_mlvi_hardening; 198 } else if (Args.hasFlag(options::OPT_mlvi_cfi, options::OPT_mno_lvi_cfi, 199 false)) { 200 Features.push_back("+lvi-cfi"); 201 LVIOpt = options::OPT_mlvi_cfi; 202 } 203 204 if (Args.hasFlag(options::OPT_m_seses, options::OPT_mno_seses, false)) { 205 if (LVIOpt == options::OPT_mlvi_hardening) 206 D.Diag(diag::err_drv_argument_not_allowed_with) 207 << D.getOpts().getOptionName(options::OPT_mlvi_hardening) 208 << D.getOpts().getOptionName(options::OPT_m_seses); 209 210 if (SpectreOpt != clang::driver::options::ID::OPT_INVALID) 211 D.Diag(diag::err_drv_argument_not_allowed_with) 212 << D.getOpts().getOptionName(SpectreOpt) 213 << D.getOpts().getOptionName(options::OPT_m_seses); 214 215 Features.push_back("+seses"); 216 if (!Args.hasArg(options::OPT_mno_lvi_cfi)) { 217 Features.push_back("+lvi-cfi"); 218 LVIOpt = options::OPT_mlvi_cfi; 219 } 220 } 221 222 if (SpectreOpt != clang::driver::options::ID::OPT_INVALID && 223 LVIOpt != clang::driver::options::ID::OPT_INVALID) { 224 D.Diag(diag::err_drv_argument_not_allowed_with) 225 << D.getOpts().getOptionName(SpectreOpt) 226 << D.getOpts().getOptionName(LVIOpt); 227 } 228 229 // Now add any that the user explicitly requested on the command line, 230 // which may override the defaults. 231 for (const Arg *A : Args.filtered(options::OPT_m_x86_Features_Group, 232 options::OPT_mgeneral_regs_only)) { 233 StringRef Name = A->getOption().getName(); 234 A->claim(); 235 236 // Skip over "-m". 237 assert(Name.startswith("m") && "Invalid feature name."); 238 Name = Name.substr(1); 239 240 // Replace -mgeneral-regs-only with -x87, -mmx, -sse 241 if (A->getOption().getID() == options::OPT_mgeneral_regs_only) { 242 Features.insert(Features.end(), {"-x87", "-mmx", "-sse"}); 243 continue; 244 } 245 246 bool IsNegative = Name.startswith("no-"); 247 if (IsNegative) 248 Name = Name.substr(3); 249 Features.push_back(Args.MakeArgString((IsNegative ? "-" : "+") + Name)); 250 } 251 252 // Enable/disable straight line speculation hardening. 253 if (Arg *A = Args.getLastArg(options::OPT_mharden_sls_EQ)) { 254 StringRef Scope = A->getValue(); 255 if (Scope == "all") { 256 Features.push_back("+harden-sls-ijmp"); 257 Features.push_back("+harden-sls-ret"); 258 } else if (Scope == "return") { 259 Features.push_back("+harden-sls-ret"); 260 } else if (Scope == "indirect-jmp") { 261 Features.push_back("+harden-sls-ijmp"); 262 } else if (Scope != "none") { 263 D.Diag(diag::err_drv_unsupported_option_argument) 264 << A->getSpelling() << Scope; 265 } 266 } 267 } 268