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/TargetParser/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(Ids: 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(Ids: 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(List: { |
51 | {"IA32" , "i386" }, |
52 | {"SSE" , "pentium3" }, |
53 | {"SSE2" , "pentium4" }, |
54 | }); |
55 | } |
56 | StringRef CPU = ArchMap.lookup(Key: A->getValue()); |
57 | if (CPU.empty()) { |
58 | std::vector<StringRef> ValidArchs{ArchMap.keys().begin(), |
59 | ArchMap.keys().end()}; |
60 | sort(C&: ValidArchs); |
61 | D.Diag(DiagID: diag::warn_drv_invalid_arch_name_with_suggestion) |
62 | << A->getValue() << (Triple.getArch() == llvm::Triple::x86) |
63 | << join(R&: ValidArchs, Separator: ", " ); |
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(Major: 10, Minor: 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 | // Claim and report unsupported -mabi=. Note: we don't support "sysv_abi" or |
123 | // "ms_abi" as default function attributes. |
124 | if (const Arg *A = Args.getLastArg(Ids: clang::driver::options::OPT_mabi_EQ)) { |
125 | StringRef DefaultAbi = Triple.isOSWindows() ? "ms" : "sysv" ; |
126 | if (A->getValue() != DefaultAbi) |
127 | D.Diag(DiagID: diag::err_drv_unsupported_opt_for_target) |
128 | << A->getSpelling() << Triple.getTriple(); |
129 | } |
130 | |
131 | // If -march=native, autodetect the feature list. |
132 | if (const Arg *A = Args.getLastArg(Ids: clang::driver::options::OPT_march_EQ)) { |
133 | if (StringRef(A->getValue()) == "native" ) { |
134 | for (auto &F : llvm::sys::getHostCPUFeatures()) |
135 | Features.push_back( |
136 | x: Args.MakeArgString(Str: (F.second ? "+" : "-" ) + F.first())); |
137 | } |
138 | } |
139 | |
140 | if (Triple.getArchName() == "x86_64h" ) { |
141 | // x86_64h implies quite a few of the more modern subtarget features |
142 | // for Haswell class CPUs, but not all of them. Opt-out of a few. |
143 | Features.push_back(x: "-rdrnd" ); |
144 | Features.push_back(x: "-aes" ); |
145 | Features.push_back(x: "-pclmul" ); |
146 | Features.push_back(x: "-rtm" ); |
147 | Features.push_back(x: "-fsgsbase" ); |
148 | } |
149 | |
150 | const llvm::Triple::ArchType ArchType = Triple.getArch(); |
151 | // Add features to be compatible with gcc for Android. |
152 | if (Triple.isAndroid()) { |
153 | if (ArchType == llvm::Triple::x86_64) { |
154 | Features.push_back(x: "+sse4.2" ); |
155 | Features.push_back(x: "+popcnt" ); |
156 | Features.push_back(x: "+cx16" ); |
157 | } else |
158 | Features.push_back(x: "+ssse3" ); |
159 | } |
160 | |
161 | // Translate the high level `-mretpoline` flag to the specific target feature |
162 | // flags. We also detect if the user asked for retpoline external thunks but |
163 | // failed to ask for retpolines themselves (through any of the different |
164 | // flags). This is a bit hacky but keeps existing usages working. We should |
165 | // consider deprecating this and instead warn if the user requests external |
166 | // retpoline thunks and *doesn't* request some form of retpolines. |
167 | auto SpectreOpt = clang::driver::options::ID::OPT_INVALID; |
168 | if (Args.hasArgNoClaim(Ids: options::OPT_mretpoline, Ids: options::OPT_mno_retpoline, |
169 | Ids: options::OPT_mspeculative_load_hardening, |
170 | Ids: options::OPT_mno_speculative_load_hardening)) { |
171 | if (Args.hasFlag(Pos: options::OPT_mretpoline, Neg: options::OPT_mno_retpoline, |
172 | Default: false)) { |
173 | Features.push_back(x: "+retpoline-indirect-calls" ); |
174 | Features.push_back(x: "+retpoline-indirect-branches" ); |
175 | SpectreOpt = options::OPT_mretpoline; |
176 | } else if (Args.hasFlag(Pos: options::OPT_mspeculative_load_hardening, |
177 | Neg: options::OPT_mno_speculative_load_hardening, |
178 | Default: false)) { |
179 | // On x86, speculative load hardening relies on at least using retpolines |
180 | // for indirect calls. |
181 | Features.push_back(x: "+retpoline-indirect-calls" ); |
182 | SpectreOpt = options::OPT_mspeculative_load_hardening; |
183 | } |
184 | } else if (Args.hasFlag(Pos: options::OPT_mretpoline_external_thunk, |
185 | Neg: options::OPT_mno_retpoline_external_thunk, Default: false)) { |
186 | // FIXME: Add a warning about failing to specify `-mretpoline` and |
187 | // eventually switch to an error here. |
188 | Features.push_back(x: "+retpoline-indirect-calls" ); |
189 | Features.push_back(x: "+retpoline-indirect-branches" ); |
190 | SpectreOpt = options::OPT_mretpoline_external_thunk; |
191 | } |
192 | |
193 | auto LVIOpt = clang::driver::options::ID::OPT_INVALID; |
194 | if (Args.hasFlag(Pos: options::OPT_mlvi_hardening, Neg: options::OPT_mno_lvi_hardening, |
195 | Default: false)) { |
196 | Features.push_back(x: "+lvi-load-hardening" ); |
197 | Features.push_back(x: "+lvi-cfi" ); // load hardening implies CFI protection |
198 | LVIOpt = options::OPT_mlvi_hardening; |
199 | } else if (Args.hasFlag(Pos: options::OPT_mlvi_cfi, Neg: options::OPT_mno_lvi_cfi, |
200 | Default: false)) { |
201 | Features.push_back(x: "+lvi-cfi" ); |
202 | LVIOpt = options::OPT_mlvi_cfi; |
203 | } |
204 | |
205 | if (Args.hasFlag(Pos: options::OPT_m_seses, Neg: options::OPT_mno_seses, Default: false)) { |
206 | if (LVIOpt == options::OPT_mlvi_hardening) |
207 | D.Diag(DiagID: diag::err_drv_argument_not_allowed_with) |
208 | << D.getOpts().getOptionName(id: options::OPT_mlvi_hardening) |
209 | << D.getOpts().getOptionName(id: options::OPT_m_seses); |
210 | |
211 | if (SpectreOpt != clang::driver::options::ID::OPT_INVALID) |
212 | D.Diag(DiagID: diag::err_drv_argument_not_allowed_with) |
213 | << D.getOpts().getOptionName(id: SpectreOpt) |
214 | << D.getOpts().getOptionName(id: options::OPT_m_seses); |
215 | |
216 | Features.push_back(x: "+seses" ); |
217 | if (!Args.hasArg(Ids: options::OPT_mno_lvi_cfi)) { |
218 | Features.push_back(x: "+lvi-cfi" ); |
219 | LVIOpt = options::OPT_mlvi_cfi; |
220 | } |
221 | } |
222 | |
223 | if (SpectreOpt != clang::driver::options::ID::OPT_INVALID && |
224 | LVIOpt != clang::driver::options::ID::OPT_INVALID) { |
225 | D.Diag(DiagID: diag::err_drv_argument_not_allowed_with) |
226 | << D.getOpts().getOptionName(id: SpectreOpt) |
227 | << D.getOpts().getOptionName(id: LVIOpt); |
228 | } |
229 | |
230 | for (const Arg *A : Args.filtered(Ids: options::OPT_m_x86_AVX10_Features_Group)) { |
231 | StringRef Name = A->getOption().getName(); |
232 | A->claim(); |
233 | |
234 | // Skip over "-m". |
235 | assert(Name.starts_with("m" ) && "Invalid feature name." ); |
236 | Name = Name.substr(Start: 1); |
237 | |
238 | bool IsNegative = Name.consume_front(Prefix: "no-" ); |
239 | |
240 | #ifndef NDEBUG |
241 | assert(Name.starts_with("avx10." ) && "Invalid AVX10 feature name." ); |
242 | StringRef Version, Width; |
243 | std::tie(Version, Width) = Name.substr(6).split('-'); |
244 | assert(Version == "1" && "Invalid AVX10 feature name." ); |
245 | assert((Width == "256" || Width == "512" ) && "Invalid AVX10 feature name." ); |
246 | #endif |
247 | |
248 | Features.push_back(x: Args.MakeArgString(Str: (IsNegative ? "-" : "+" ) + Name)); |
249 | } |
250 | |
251 | // Now add any that the user explicitly requested on the command line, |
252 | // which may override the defaults. |
253 | for (const Arg *A : Args.filtered(Ids: options::OPT_m_x86_Features_Group, |
254 | Ids: options::OPT_mgeneral_regs_only)) { |
255 | StringRef Name = A->getOption().getName(); |
256 | A->claim(); |
257 | |
258 | // Skip over "-m". |
259 | assert(Name.starts_with("m" ) && "Invalid feature name." ); |
260 | Name = Name.substr(Start: 1); |
261 | |
262 | // Replace -mgeneral-regs-only with -x87, -mmx, -sse |
263 | if (A->getOption().getID() == options::OPT_mgeneral_regs_only) { |
264 | Features.insert(position: Features.end(), l: {"-x87" , "-mmx" , "-sse" }); |
265 | continue; |
266 | } |
267 | |
268 | bool IsNegative = Name.starts_with(Prefix: "no-" ); |
269 | if (A->getOption().matches(ID: options::OPT_mapx_features_EQ) || |
270 | A->getOption().matches(ID: options::OPT_mno_apx_features_EQ)) { |
271 | |
272 | for (StringRef Value : A->getValues()) { |
273 | if (Value == "egpr" || Value == "push2pop2" || Value == "ppx" || |
274 | Value == "ndd" || Value == "ccmp" || Value == "nf" || |
275 | Value == "cf" || Value == "zu" ) { |
276 | Features.push_back( |
277 | x: Args.MakeArgString(Str: (IsNegative ? "-" : "+" ) + Value)); |
278 | continue; |
279 | } |
280 | D.Diag(DiagID: clang::diag::err_drv_unsupported_option_argument) |
281 | << A->getSpelling() << Value; |
282 | } |
283 | continue; |
284 | } |
285 | if (IsNegative) |
286 | Name = Name.substr(Start: 3); |
287 | Features.push_back(x: Args.MakeArgString(Str: (IsNegative ? "-" : "+" ) + Name)); |
288 | } |
289 | |
290 | // Enable/disable straight line speculation hardening. |
291 | if (Arg *A = Args.getLastArg(Ids: options::OPT_mharden_sls_EQ)) { |
292 | StringRef Scope = A->getValue(); |
293 | if (Scope == "all" ) { |
294 | Features.push_back(x: "+harden-sls-ijmp" ); |
295 | Features.push_back(x: "+harden-sls-ret" ); |
296 | } else if (Scope == "return" ) { |
297 | Features.push_back(x: "+harden-sls-ret" ); |
298 | } else if (Scope == "indirect-jmp" ) { |
299 | Features.push_back(x: "+harden-sls-ijmp" ); |
300 | } else if (Scope != "none" ) { |
301 | D.Diag(DiagID: diag::err_drv_unsupported_option_argument) |
302 | << A->getSpelling() << Scope; |
303 | } |
304 | } |
305 | |
306 | // -mno-gather, -mno-scatter support |
307 | if (Args.hasArg(Ids: options::OPT_mno_gather)) |
308 | Features.push_back(x: "+prefer-no-gather" ); |
309 | if (Args.hasArg(Ids: options::OPT_mno_scatter)) |
310 | Features.push_back(x: "+prefer-no-scatter" ); |
311 | if (Args.hasArg(Ids: options::OPT_mapx_inline_asm_use_gpr32)) |
312 | Features.push_back(x: "+inline-asm-use-gpr32" ); |
313 | |
314 | // Warn for removed 3dnow support |
315 | if (const Arg *A = |
316 | Args.getLastArg(Ids: options::OPT_m3dnowa, Ids: options::OPT_mno_3dnowa, |
317 | Ids: options::OPT_mno_3dnow)) { |
318 | if (A->getOption().matches(ID: options::OPT_m3dnowa)) |
319 | D.Diag(DiagID: diag::warn_drv_clang_unsupported) << A->getAsString(Args); |
320 | } |
321 | if (const Arg *A = |
322 | Args.getLastArg(Ids: options::OPT_m3dnow, Ids: options::OPT_mno_3dnow)) { |
323 | if (A->getOption().matches(ID: options::OPT_m3dnow)) |
324 | D.Diag(DiagID: diag::warn_drv_clang_unsupported) << A->getAsString(Args); |
325 | } |
326 | } |
327 | |