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/StringSwitch.h"
15 #include "llvm/Option/ArgList.h"
16 #include "llvm/Support/Host.h"
17 
18 using namespace clang::driver;
19 using namespace clang::driver::tools;
20 using namespace clang;
21 using namespace llvm::opt;
22 
23 std::string x86::getX86TargetCPU(const ArgList &Args,
24                                  const llvm::Triple &Triple) {
25   if (const Arg *A = Args.getLastArg(clang::driver::options::OPT_march_EQ)) {
26     StringRef CPU = A->getValue();
27     if (CPU != "native")
28       return std::string(CPU);
29 
30     // FIXME: Reject attempts to use -march=native unless the target matches
31     // the host.
32     //
33     // FIXME: We should also incorporate the detected target features for use
34     // with -native.
35     CPU = llvm::sys::getHostCPUName();
36     if (!CPU.empty() && CPU != "generic")
37       return std::string(CPU);
38   }
39 
40   if (const Arg *A = Args.getLastArgNoClaim(options::OPT__SLASH_arch)) {
41     // Mapping built by looking at lib/Basic's X86TargetInfo::initFeatureMap().
42     StringRef Arch = A->getValue();
43     StringRef CPU;
44     if (Triple.getArch() == llvm::Triple::x86) {  // 32-bit-only /arch: flags.
45       CPU = llvm::StringSwitch<StringRef>(Arch)
46                 .Case("IA32", "i386")
47                 .Case("SSE", "pentium3")
48                 .Case("SSE2", "pentium4")
49                 .Default("");
50     }
51     if (CPU.empty()) {  // 32-bit and 64-bit /arch: flags.
52       CPU = llvm::StringSwitch<StringRef>(Arch)
53                 .Case("AVX", "sandybridge")
54                 .Case("AVX2", "haswell")
55                 .Case("AVX512F", "knl")
56                 .Case("AVX512", "skylake-avx512")
57                 .Default("");
58     }
59     if (!CPU.empty()) {
60       A->claim();
61       return std::string(CPU);
62     }
63   }
64 
65   // Select the default CPU if none was given (or detection failed).
66 
67   if (!Triple.isX86())
68     return ""; // This routine is only handling x86 targets.
69 
70   bool Is64Bit = Triple.getArch() == llvm::Triple::x86_64;
71 
72   // FIXME: Need target hooks.
73   if (Triple.isOSDarwin()) {
74     if (Triple.getArchName() == "x86_64h")
75       return "core-avx2";
76     // macosx10.12 drops support for all pre-Penryn Macs.
77     // Simulators can still run on 10.11 though, like Xcode.
78     if (Triple.isMacOSX() && !Triple.isOSVersionLT(10, 12))
79       return "penryn";
80     // The oldest x86_64 Macs have core2/Merom; the oldest x86 Macs have Yonah.
81     return Is64Bit ? "core2" : "yonah";
82   }
83 
84   // Set up default CPU name for PS4 compilers.
85   if (Triple.isPS4CPU())
86     return "btver2";
87 
88   // On Android use targets compatible with gcc
89   if (Triple.isAndroid())
90     return Is64Bit ? "x86-64" : "i686";
91 
92   // Everything else goes to x86-64 in 64-bit mode.
93   if (Is64Bit)
94     return "x86-64";
95 
96   switch (Triple.getOS()) {
97   case llvm::Triple::NetBSD:
98     return "i486";
99   case llvm::Triple::Haiku:
100   case llvm::Triple::OpenBSD:
101     return "i586";
102   case llvm::Triple::FreeBSD:
103     return "i686";
104   default:
105     // Fallback to p4.
106     return "pentium4";
107   }
108 }
109 
110 void x86::getX86TargetFeatures(const Driver &D, const llvm::Triple &Triple,
111                                const ArgList &Args,
112                                std::vector<StringRef> &Features) {
113   // If -march=native, autodetect the feature list.
114   if (const Arg *A = Args.getLastArg(clang::driver::options::OPT_march_EQ)) {
115     if (StringRef(A->getValue()) == "native") {
116       llvm::StringMap<bool> HostFeatures;
117       if (llvm::sys::getHostCPUFeatures(HostFeatures))
118         for (auto &F : HostFeatures)
119           Features.push_back(
120               Args.MakeArgString((F.second ? "+" : "-") + F.first()));
121     }
122   }
123 
124   if (Triple.getArchName() == "x86_64h") {
125     // x86_64h implies quite a few of the more modern subtarget features
126     // for Haswell class CPUs, but not all of them. Opt-out of a few.
127     Features.push_back("-rdrnd");
128     Features.push_back("-aes");
129     Features.push_back("-pclmul");
130     Features.push_back("-rtm");
131     Features.push_back("-fsgsbase");
132   }
133 
134   const llvm::Triple::ArchType ArchType = Triple.getArch();
135   // Add features to be compatible with gcc for Android.
136   if (Triple.isAndroid()) {
137     if (ArchType == llvm::Triple::x86_64) {
138       Features.push_back("+sse4.2");
139       Features.push_back("+popcnt");
140       Features.push_back("+cx16");
141     } else
142       Features.push_back("+ssse3");
143   }
144 
145   // Translate the high level `-mretpoline` flag to the specific target feature
146   // flags. We also detect if the user asked for retpoline external thunks but
147   // failed to ask for retpolines themselves (through any of the different
148   // flags). This is a bit hacky but keeps existing usages working. We should
149   // consider deprecating this and instead warn if the user requests external
150   // retpoline thunks and *doesn't* request some form of retpolines.
151   auto SpectreOpt = clang::driver::options::ID::OPT_INVALID;
152   if (Args.hasArgNoClaim(options::OPT_mretpoline, options::OPT_mno_retpoline,
153                          options::OPT_mspeculative_load_hardening,
154                          options::OPT_mno_speculative_load_hardening)) {
155     if (Args.hasFlag(options::OPT_mretpoline, options::OPT_mno_retpoline,
156                      false)) {
157       Features.push_back("+retpoline-indirect-calls");
158       Features.push_back("+retpoline-indirect-branches");
159       SpectreOpt = options::OPT_mretpoline;
160     } else if (Args.hasFlag(options::OPT_mspeculative_load_hardening,
161                             options::OPT_mno_speculative_load_hardening,
162                             false)) {
163       // On x86, speculative load hardening relies on at least using retpolines
164       // for indirect calls.
165       Features.push_back("+retpoline-indirect-calls");
166       SpectreOpt = options::OPT_mspeculative_load_hardening;
167     }
168   } else if (Args.hasFlag(options::OPT_mretpoline_external_thunk,
169                           options::OPT_mno_retpoline_external_thunk, false)) {
170     // FIXME: Add a warning about failing to specify `-mretpoline` and
171     // eventually switch to an error here.
172     Features.push_back("+retpoline-indirect-calls");
173     Features.push_back("+retpoline-indirect-branches");
174     SpectreOpt = options::OPT_mretpoline_external_thunk;
175   }
176 
177   auto LVIOpt = clang::driver::options::ID::OPT_INVALID;
178   if (Args.hasFlag(options::OPT_mlvi_hardening, options::OPT_mno_lvi_hardening,
179                    false)) {
180     Features.push_back("+lvi-load-hardening");
181     Features.push_back("+lvi-cfi"); // load hardening implies CFI protection
182     LVIOpt = options::OPT_mlvi_hardening;
183   } else if (Args.hasFlag(options::OPT_mlvi_cfi, options::OPT_mno_lvi_cfi,
184                           false)) {
185     Features.push_back("+lvi-cfi");
186     LVIOpt = options::OPT_mlvi_cfi;
187   }
188 
189   if (Args.hasFlag(options::OPT_m_seses, options::OPT_mno_seses, false)) {
190     if (LVIOpt == options::OPT_mlvi_hardening)
191       D.Diag(diag::err_drv_argument_not_allowed_with)
192           << D.getOpts().getOptionName(options::OPT_mlvi_hardening)
193           << D.getOpts().getOptionName(options::OPT_m_seses);
194 
195     if (SpectreOpt != clang::driver::options::ID::OPT_INVALID)
196       D.Diag(diag::err_drv_argument_not_allowed_with)
197           << D.getOpts().getOptionName(SpectreOpt)
198           << D.getOpts().getOptionName(options::OPT_m_seses);
199 
200     Features.push_back("+seses");
201     if (!Args.hasArg(options::OPT_mno_lvi_cfi)) {
202       Features.push_back("+lvi-cfi");
203       LVIOpt = options::OPT_mlvi_cfi;
204     }
205   }
206 
207   if (SpectreOpt != clang::driver::options::ID::OPT_INVALID &&
208       LVIOpt != clang::driver::options::ID::OPT_INVALID) {
209     D.Diag(diag::err_drv_argument_not_allowed_with)
210         << D.getOpts().getOptionName(SpectreOpt)
211         << D.getOpts().getOptionName(LVIOpt);
212   }
213 
214   // Now add any that the user explicitly requested on the command line,
215   // which may override the defaults.
216   for (const Arg *A : Args.filtered(options::OPT_m_x86_Features_Group,
217                                     options::OPT_mgeneral_regs_only)) {
218     StringRef Name = A->getOption().getName();
219     A->claim();
220 
221     // Skip over "-m".
222     assert(Name.startswith("m") && "Invalid feature name.");
223     Name = Name.substr(1);
224 
225     // Replace -mgeneral-regs-only with -x87, -mmx, -sse
226     if (A->getOption().getID() == options::OPT_mgeneral_regs_only) {
227       Features.insert(Features.end(), {"-x87", "-mmx", "-sse"});
228       continue;
229     }
230 
231     bool IsNegative = Name.startswith("no-");
232     if (IsNegative)
233       Name = Name.substr(3);
234     Features.push_back(Args.MakeArgString((IsNegative ? "-" : "+") + Name));
235   }
236 }
237