xref: /freebsd/contrib/llvm-project/clang/lib/Driver/ToolChains/Arch/X86.cpp (revision 700637cbb5e582861067a11aaca4d053546871d2)
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/Driver/Options.h"
12 #include "llvm/ADT/StringExtras.h"
13 #include "llvm/ADT/StringMap.h"
14 #include "llvm/Option/ArgList.h"
15 #include "llvm/TargetParser/Host.h"
16 
17 using namespace clang::driver;
18 using namespace clang::driver::tools;
19 using namespace clang;
20 using namespace llvm::opt;
21 
getX86TargetCPU(const Driver & D,const ArgList & Args,const llvm::Triple & Triple)22 std::string x86::getX86TargetCPU(const Driver &D, const ArgList &Args,
23                                  const llvm::Triple &Triple) {
24   if (const Arg *A = Args.getLastArg(clang::driver::options::OPT_march_EQ)) {
25     StringRef CPU = A->getValue();
26     if (CPU != "native")
27       return std::string(CPU);
28 
29     // FIXME: Reject attempts to use -march=native unless the target matches
30     // the host.
31     CPU = llvm::sys::getHostCPUName();
32     if (!CPU.empty() && CPU != "generic")
33       return std::string(CPU);
34   }
35 
36   if (const Arg *A = Args.getLastArg(options::OPT__SLASH_arch)) {
37     // Mapping built by looking at lib/Basic's X86TargetInfo::initFeatureMap().
38     // The keys are case-sensitive; this matches link.exe.
39     // 32-bit and 64-bit /arch: flags.
40     llvm::StringMap<StringRef> ArchMap({
41         {"AVX", "sandybridge"},
42         {"AVX2", "haswell"},
43         {"AVX512F", "knl"},
44         {"AVX512", "skylake-avx512"},
45     });
46     if (Triple.getArch() == llvm::Triple::x86) {
47       // 32-bit-only /arch: flags.
48       ArchMap.insert({
49           {"IA32", "i386"},
50           {"SSE", "pentium3"},
51           {"SSE2", "pentium4"},
52       });
53     }
54     StringRef CPU = ArchMap.lookup(A->getValue());
55     if (CPU.empty()) {
56       std::vector<StringRef> ValidArchs{ArchMap.keys().begin(),
57                                         ArchMap.keys().end()};
58       sort(ValidArchs);
59       D.Diag(diag::warn_drv_invalid_arch_name_with_suggestion)
60           << A->getValue() << (Triple.getArch() == llvm::Triple::x86)
61           << join(ValidArchs, ", ");
62     }
63     return std::string(CPU);
64   }
65 
66   // Select the default CPU if none was given (or detection failed).
67 
68   if (!Triple.isX86())
69     return ""; // This routine is only handling x86 targets.
70 
71   bool Is64Bit = Triple.getArch() == llvm::Triple::x86_64;
72 
73   // FIXME: Need target hooks.
74   if (Triple.isOSDarwin()) {
75     if (Triple.getArchName() == "x86_64h")
76       return "core-avx2";
77     // macosx10.12 drops support for all pre-Penryn Macs.
78     // Simulators can still run on 10.11 though, like Xcode.
79     if (Triple.isMacOSX() && !Triple.isOSVersionLT(10, 12))
80       return "penryn";
81 
82     if (Triple.isDriverKit())
83       return "nehalem";
84 
85     // The oldest x86_64 Macs have core2/Merom; the oldest x86 Macs have Yonah.
86     return Is64Bit ? "core2" : "yonah";
87   }
88 
89   // Set up default CPU name for PS4/PS5 compilers.
90   if (Triple.isPS4())
91     return "btver2";
92   if (Triple.isPS5())
93     return "znver2";
94 
95   // On Android use targets compatible with gcc
96   if (Triple.isAndroid())
97     return Is64Bit ? "x86-64" : "i686";
98 
99   // Everything else goes to x86-64 in 64-bit mode.
100   if (Is64Bit)
101     return "x86-64";
102 
103   switch (Triple.getOS()) {
104   case llvm::Triple::NetBSD:
105     return "i486";
106   case llvm::Triple::Haiku:
107   case llvm::Triple::OpenBSD:
108     return "i586";
109   case llvm::Triple::FreeBSD:
110     return "i686";
111   default:
112     // Fallback to p4.
113     return "pentium4";
114   }
115 }
116 
getX86TargetFeatures(const Driver & D,const llvm::Triple & Triple,const ArgList & Args,std::vector<StringRef> & Features)117 void x86::getX86TargetFeatures(const Driver &D, const llvm::Triple &Triple,
118                                const ArgList &Args,
119                                std::vector<StringRef> &Features) {
120   // Claim and report unsupported -mabi=. Note: we don't support "sysv_abi" or
121   // "ms_abi" as default function attributes.
122   if (const Arg *A = Args.getLastArg(clang::driver::options::OPT_mabi_EQ)) {
123     StringRef DefaultAbi =
124         (Triple.isOSWindows() || Triple.isUEFI()) ? "ms" : "sysv";
125     if (A->getValue() != DefaultAbi)
126       D.Diag(diag::err_drv_unsupported_opt_for_target)
127           << A->getSpelling() << Triple.getTriple();
128   }
129 
130   // If -march=native, autodetect the feature list.
131   if (const Arg *A = Args.getLastArg(clang::driver::options::OPT_march_EQ)) {
132     if (StringRef(A->getValue()) == "native") {
133       for (auto &F : llvm::sys::getHostCPUFeatures())
134         Features.push_back(
135             Args.MakeArgString((F.second ? "+" : "-") + F.first()));
136     }
137   }
138 
139   if (Triple.getArchName() == "x86_64h") {
140     // x86_64h implies quite a few of the more modern subtarget features
141     // for Haswell class CPUs, but not all of them. Opt-out of a few.
142     Features.push_back("-rdrnd");
143     Features.push_back("-aes");
144     Features.push_back("-pclmul");
145     Features.push_back("-rtm");
146     Features.push_back("-fsgsbase");
147   }
148 
149   const llvm::Triple::ArchType ArchType = Triple.getArch();
150   // Add features to be compatible with gcc for Android.
151   if (Triple.isAndroid()) {
152     if (ArchType == llvm::Triple::x86_64) {
153       Features.push_back("+sse4.2");
154       Features.push_back("+popcnt");
155       Features.push_back("+cx16");
156     } else
157       Features.push_back("+ssse3");
158   }
159 
160   // Translate the high level `-mretpoline` flag to the specific target feature
161   // flags. We also detect if the user asked for retpoline external thunks but
162   // failed to ask for retpolines themselves (through any of the different
163   // flags). This is a bit hacky but keeps existing usages working. We should
164   // consider deprecating this and instead warn if the user requests external
165   // retpoline thunks and *doesn't* request some form of retpolines.
166   auto SpectreOpt = clang::driver::options::ID::OPT_INVALID;
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   for (const Arg *A : Args.filtered(options::OPT_m_x86_AVX10_Features_Group)) {
230     StringRef Name = A->getOption().getName();
231     A->claim();
232 
233     // Skip over "-m".
234     assert(Name.starts_with("m") && "Invalid feature name.");
235     Name = Name.substr(1);
236 
237     bool IsNegative = Name.consume_front("no-");
238 
239     StringRef Version, Width;
240     std::tie(Version, Width) = Name.substr(6).split('-');
241     assert(Name.starts_with("avx10.") && "Invalid AVX10 feature name.");
242     assert((Version == "1" || Version == "2") && "Invalid AVX10 feature name.");
243 
244     if (Width == "") {
245       if (IsNegative)
246         Features.push_back(Args.MakeArgString("-" + Name + "-256"));
247       else
248         Features.push_back(Args.MakeArgString("+" + Name + "-512"));
249     } else {
250       if (Width == "512")
251         D.Diag(diag::warn_drv_deprecated_arg) << Name << 1 << Name.drop_back(4);
252       else if (Width == "256")
253         D.Diag(diag::warn_drv_deprecated_custom)
254             << Name
255             << "no alternative argument provided because "
256                "AVX10/256 is not supported and will be removed";
257       else
258         assert((Width == "256" || Width == "512") && "Invalid vector length.");
259       Features.push_back(Args.MakeArgString((IsNegative ? "-" : "+") + Name));
260     }
261   }
262 
263   // Now add any that the user explicitly requested on the command line,
264   // which may override the defaults.
265   for (const Arg *A : Args.filtered(options::OPT_m_x86_Features_Group,
266                                     options::OPT_mgeneral_regs_only)) {
267     StringRef Name = A->getOption().getName();
268     A->claim();
269 
270     // Skip over "-m".
271     assert(Name.starts_with("m") && "Invalid feature name.");
272     Name = Name.substr(1);
273 
274     // Replace -mgeneral-regs-only with -x87, -mmx, -sse
275     if (A->getOption().getID() == options::OPT_mgeneral_regs_only) {
276       Features.insert(Features.end(), {"-x87", "-mmx", "-sse"});
277       continue;
278     }
279 
280     bool IsNegative = Name.starts_with("no-");
281 
282     bool Not64Bit = ArchType != llvm::Triple::x86_64;
283     if (Not64Bit && Name == "uintr")
284       D.Diag(diag::err_drv_unsupported_opt_for_target)
285           << A->getSpelling() << Triple.getTriple();
286 
287     if (A->getOption().matches(options::OPT_mevex512) ||
288         A->getOption().matches(options::OPT_mno_evex512))
289       D.Diag(diag::warn_drv_deprecated_custom)
290           << Name
291           << "no alternative argument provided because "
292              "AVX10/256 is not supported and will be removed";
293 
294     if (A->getOption().matches(options::OPT_mapx_features_EQ) ||
295         A->getOption().matches(options::OPT_mno_apx_features_EQ)) {
296 
297       if (Not64Bit && !IsNegative)
298         D.Diag(diag::err_drv_unsupported_opt_for_target)
299             << StringRef(A->getSpelling().str() + "|-mapxf")
300             << Triple.getTriple();
301 
302       for (StringRef Value : A->getValues()) {
303         if (Value != "egpr" && Value != "push2pop2" && Value != "ppx" &&
304             Value != "ndd" && Value != "ccmp" && Value != "nf" &&
305             Value != "cf" && Value != "zu")
306           D.Diag(clang::diag::err_drv_unsupported_option_argument)
307               << A->getSpelling() << Value;
308 
309         Features.push_back(
310             Args.MakeArgString((IsNegative ? "-" : "+") + Value));
311       }
312       continue;
313     }
314     if (IsNegative)
315       Name = Name.substr(3);
316     Features.push_back(Args.MakeArgString((IsNegative ? "-" : "+") + Name));
317   }
318 
319   // Enable/disable straight line speculation hardening.
320   if (Arg *A = Args.getLastArg(options::OPT_mharden_sls_EQ)) {
321     StringRef Scope = A->getValue();
322     if (Scope == "all") {
323       Features.push_back("+harden-sls-ijmp");
324       Features.push_back("+harden-sls-ret");
325     } else if (Scope == "return") {
326       Features.push_back("+harden-sls-ret");
327     } else if (Scope == "indirect-jmp") {
328       Features.push_back("+harden-sls-ijmp");
329     } else if (Scope != "none") {
330       D.Diag(diag::err_drv_unsupported_option_argument)
331           << A->getSpelling() << Scope;
332     }
333   }
334 
335   // -mno-gather, -mno-scatter support
336   if (Args.hasArg(options::OPT_mno_gather))
337     Features.push_back("+prefer-no-gather");
338   if (Args.hasArg(options::OPT_mno_scatter))
339     Features.push_back("+prefer-no-scatter");
340   if (Args.hasArg(options::OPT_mapx_inline_asm_use_gpr32))
341     Features.push_back("+inline-asm-use-gpr32");
342 
343   // Warn for removed 3dnow support
344   if (const Arg *A =
345           Args.getLastArg(options::OPT_m3dnowa, options::OPT_mno_3dnowa,
346                           options::OPT_mno_3dnow)) {
347     if (A->getOption().matches(options::OPT_m3dnowa))
348       D.Diag(diag::warn_drv_clang_unsupported) << A->getAsString(Args);
349   }
350   if (const Arg *A =
351           Args.getLastArg(options::OPT_m3dnow, options::OPT_mno_3dnow)) {
352     if (A->getOption().matches(options::OPT_m3dnow))
353       D.Diag(diag::warn_drv_clang_unsupported) << A->getAsString(Args);
354   }
355 }
356