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 (Args.hasArgNoClaim(options::OPT_mretpoline, options::OPT_mno_retpoline, 162 options::OPT_mspeculative_load_hardening, 163 options::OPT_mno_speculative_load_hardening)) { 164 if (Args.hasFlag(options::OPT_mretpoline, options::OPT_mno_retpoline, 165 false)) { 166 Features.push_back("+retpoline-indirect-calls"); 167 Features.push_back("+retpoline-indirect-branches"); 168 SpectreOpt = options::OPT_mretpoline; 169 } else if (Args.hasFlag(options::OPT_mspeculative_load_hardening, 170 options::OPT_mno_speculative_load_hardening, 171 false)) { 172 // On x86, speculative load hardening relies on at least using retpolines 173 // for indirect calls. 174 Features.push_back("+retpoline-indirect-calls"); 175 SpectreOpt = options::OPT_mspeculative_load_hardening; 176 } 177 } else if (Args.hasFlag(options::OPT_mretpoline_external_thunk, 178 options::OPT_mno_retpoline_external_thunk, false)) { 179 // FIXME: Add a warning about failing to specify `-mretpoline` and 180 // eventually switch to an error here. 181 Features.push_back("+retpoline-indirect-calls"); 182 Features.push_back("+retpoline-indirect-branches"); 183 SpectreOpt = options::OPT_mretpoline_external_thunk; 184 } 185 186 auto LVIOpt = clang::driver::options::ID::OPT_INVALID; 187 if (Args.hasFlag(options::OPT_mlvi_hardening, options::OPT_mno_lvi_hardening, 188 false)) { 189 Features.push_back("+lvi-load-hardening"); 190 Features.push_back("+lvi-cfi"); // load hardening implies CFI protection 191 LVIOpt = options::OPT_mlvi_hardening; 192 } else if (Args.hasFlag(options::OPT_mlvi_cfi, options::OPT_mno_lvi_cfi, 193 false)) { 194 Features.push_back("+lvi-cfi"); 195 LVIOpt = options::OPT_mlvi_cfi; 196 } 197 198 if (Args.hasFlag(options::OPT_m_seses, options::OPT_mno_seses, false)) { 199 if (LVIOpt == options::OPT_mlvi_hardening) 200 D.Diag(diag::err_drv_argument_not_allowed_with) 201 << D.getOpts().getOptionName(options::OPT_mlvi_hardening) 202 << D.getOpts().getOptionName(options::OPT_m_seses); 203 204 if (SpectreOpt != clang::driver::options::ID::OPT_INVALID) 205 D.Diag(diag::err_drv_argument_not_allowed_with) 206 << D.getOpts().getOptionName(SpectreOpt) 207 << D.getOpts().getOptionName(options::OPT_m_seses); 208 209 Features.push_back("+seses"); 210 if (!Args.hasArg(options::OPT_mno_lvi_cfi)) { 211 Features.push_back("+lvi-cfi"); 212 LVIOpt = options::OPT_mlvi_cfi; 213 } 214 } 215 216 if (SpectreOpt != clang::driver::options::ID::OPT_INVALID && 217 LVIOpt != clang::driver::options::ID::OPT_INVALID) { 218 D.Diag(diag::err_drv_argument_not_allowed_with) 219 << D.getOpts().getOptionName(SpectreOpt) 220 << D.getOpts().getOptionName(LVIOpt); 221 } 222 223 // Now add any that the user explicitly requested on the command line, 224 // which may override the defaults. 225 for (const Arg *A : Args.filtered(options::OPT_m_x86_Features_Group, 226 options::OPT_mgeneral_regs_only)) { 227 StringRef Name = A->getOption().getName(); 228 A->claim(); 229 230 // Skip over "-m". 231 assert(Name.startswith("m") && "Invalid feature name."); 232 Name = Name.substr(1); 233 234 // Replace -mgeneral-regs-only with -x87, -mmx, -sse 235 if (A->getOption().getID() == options::OPT_mgeneral_regs_only) { 236 Features.insert(Features.end(), {"-x87", "-mmx", "-sse"}); 237 continue; 238 } 239 240 bool IsNegative = Name.startswith("no-"); 241 if (IsNegative) 242 Name = Name.substr(3); 243 Features.push_back(Args.MakeArgString((IsNegative ? "-" : "+") + Name)); 244 } 245 246 // Enable/disable straight line speculation hardening. 247 if (Arg *A = Args.getLastArg(options::OPT_mharden_sls_EQ)) { 248 StringRef Scope = A->getValue(); 249 if (Scope == "all") { 250 Features.push_back("+harden-sls-ijmp"); 251 Features.push_back("+harden-sls-ret"); 252 } else if (Scope == "return") { 253 Features.push_back("+harden-sls-ret"); 254 } else if (Scope == "indirect-jmp") { 255 Features.push_back("+harden-sls-ijmp"); 256 } else if (Scope != "none") { 257 D.Diag(diag::err_drv_unsupported_option_argument) 258 << A->getSpelling() << Scope; 259 } 260 } 261 } 262