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