/Users/buildslave/jenkins/workspace/coverage/llvm-project/clang/lib/Driver/ToolChains/Arch/X86.cpp
Line | Count | Source (jump to first uncovered line) |
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 | 41.4k | const llvm::Triple &Triple) { |
26 | 41.4k | if (const Arg *A = Args.getLastArg(clang::driver::options::OPT_march_EQ)) { |
27 | 591 | StringRef CPU = A->getValue(); |
28 | 591 | if (CPU != "native") |
29 | 591 | return std::string(CPU); |
30 | | |
31 | | // FIXME: Reject attempts to use -march=native unless the target matches |
32 | | // the host. |
33 | 0 | CPU = llvm::sys::getHostCPUName(); |
34 | 0 | if (!CPU.empty() && CPU != "generic") |
35 | 0 | return std::string(CPU); |
36 | 0 | } |
37 | | |
38 | 40.8k | 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 | 40 | llvm::StringMap<StringRef> ArchMap({ |
43 | 40 | {"AVX", "sandybridge"}, |
44 | 40 | {"AVX2", "haswell"}, |
45 | 40 | {"AVX512F", "knl"}, |
46 | 40 | {"AVX512", "skylake-avx512"}, |
47 | 40 | }); |
48 | 40 | if (Triple.getArch() == llvm::Triple::x86) { |
49 | | // 32-bit-only /arch: flags. |
50 | 25 | ArchMap.insert({ |
51 | 25 | {"IA32", "i386"}, |
52 | 25 | {"SSE", "pentium3"}, |
53 | 25 | {"SSE2", "pentium4"}, |
54 | 25 | }); |
55 | 25 | } |
56 | 40 | StringRef CPU = ArchMap.lookup(A->getValue()); |
57 | 40 | if (CPU.empty()) { |
58 | 14 | std::vector<StringRef> ValidArchs{ArchMap.keys().begin(), |
59 | 14 | ArchMap.keys().end()}; |
60 | 14 | sort(ValidArchs); |
61 | 14 | D.Diag(diag::warn_drv_invalid_arch_name_with_suggestion) |
62 | 14 | << A->getValue() << (Triple.getArch() == llvm::Triple::x86) |
63 | 14 | << join(ValidArchs, ", "); |
64 | 14 | } |
65 | 40 | return std::string(CPU); |
66 | 40 | } |
67 | | |
68 | | // Select the default CPU if none was given (or detection failed). |
69 | | |
70 | 40.8k | if (!Triple.isX86()) |
71 | 0 | return ""; // This routine is only handling x86 targets. |
72 | | |
73 | 40.8k | bool Is64Bit = Triple.getArch() == llvm::Triple::x86_64; |
74 | | |
75 | | // FIXME: Need target hooks. |
76 | 40.8k | if (Triple.isOSDarwin()) { |
77 | 17.8k | if (Triple.getArchName() == "x86_64h") |
78 | 11 | 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 | 17.8k | if (Triple.isMacOSX() && !Triple.isOSVersionLT(10, 12)17.7k ) |
82 | 17.4k | return "penryn"; |
83 | | |
84 | 383 | if (Triple.isDriverKit()) |
85 | 21 | return "nehalem"; |
86 | | |
87 | | // The oldest x86_64 Macs have core2/Merom; the oldest x86 Macs have Yonah. |
88 | 362 | return Is64Bit ? "core2"313 : "yonah"49 ; |
89 | 383 | } |
90 | | |
91 | | // Set up default CPU name for PS4/PS5 compilers. |
92 | 22.9k | if (Triple.isPS4()) |
93 | 162 | return "btver2"; |
94 | 22.8k | if (Triple.isPS5()) |
95 | 128 | return "znver2"; |
96 | | |
97 | | // On Android use targets compatible with gcc |
98 | 22.6k | if (Triple.isAndroid()) |
99 | 32 | return Is64Bit ? "x86-64"17 : "i686"15 ; |
100 | | |
101 | | // Everything else goes to x86-64 in 64-bit mode. |
102 | 22.6k | if (Is64Bit) |
103 | 11.8k | return "x86-64"; |
104 | | |
105 | 10.8k | switch (Triple.getOS()) { |
106 | 10 | case llvm::Triple::NetBSD: |
107 | 10 | return "i486"; |
108 | 1 | case llvm::Triple::Haiku: |
109 | 40 | case llvm::Triple::OpenBSD: |
110 | 40 | return "i586"; |
111 | 14 | case llvm::Triple::FreeBSD: |
112 | 14 | return "i686"; |
113 | 10.7k | default: |
114 | | // Fallback to p4. |
115 | 10.7k | return "pentium4"; |
116 | 10.8k | } |
117 | 10.8k | } |
118 | | |
119 | | void x86::getX86TargetFeatures(const Driver &D, const llvm::Triple &Triple, |
120 | | const ArgList &Args, |
121 | 41.3k | 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 | 41.3k | if (const Arg *A = Args.getLastArg(clang::driver::options::OPT_mabi_EQ)) { |
125 | 5 | StringRef DefaultAbi = Triple.isOSWindows() ? "ms"3 : "sysv"2 ; |
126 | 5 | if (A->getValue() != DefaultAbi) |
127 | 2 | D.Diag(diag::err_drv_unsupported_opt_for_target) |
128 | 2 | << A->getSpelling() << Triple.getTriple(); |
129 | 5 | } |
130 | | |
131 | | // If -march=native, autodetect the feature list. |
132 | 41.3k | if (const Arg *A = Args.getLastArg(clang::driver::options::OPT_march_EQ)) { |
133 | 591 | if (StringRef(A->getValue()) == "native") { |
134 | 0 | llvm::StringMap<bool> HostFeatures; |
135 | 0 | if (llvm::sys::getHostCPUFeatures(HostFeatures)) |
136 | 0 | for (auto &F : HostFeatures) |
137 | 0 | Features.push_back( |
138 | 0 | Args.MakeArgString((F.second ? "+" : "-") + F.first())); |
139 | 0 | } |
140 | 591 | } |
141 | | |
142 | 41.3k | if (Triple.getArchName() == "x86_64h") { |
143 | | // x86_64h implies quite a few of the more modern subtarget features |
144 | | // for Haswell class CPUs, but not all of them. Opt-out of a few. |
145 | 12 | Features.push_back("-rdrnd"); |
146 | 12 | Features.push_back("-aes"); |
147 | 12 | Features.push_back("-pclmul"); |
148 | 12 | Features.push_back("-rtm"); |
149 | 12 | Features.push_back("-fsgsbase"); |
150 | 12 | } |
151 | | |
152 | 41.3k | const llvm::Triple::ArchType ArchType = Triple.getArch(); |
153 | | // Add features to be compatible with gcc for Android. |
154 | 41.3k | if (Triple.isAndroid()) { |
155 | 32 | if (ArchType == llvm::Triple::x86_64) { |
156 | 17 | Features.push_back("+sse4.2"); |
157 | 17 | Features.push_back("+popcnt"); |
158 | 17 | Features.push_back("+cx16"); |
159 | 17 | } else |
160 | 15 | Features.push_back("+ssse3"); |
161 | 32 | } |
162 | | |
163 | | // Translate the high level `-mretpoline` flag to the specific target feature |
164 | | // flags. We also detect if the user asked for retpoline external thunks but |
165 | | // failed to ask for retpolines themselves (through any of the different |
166 | | // flags). This is a bit hacky but keeps existing usages working. We should |
167 | | // consider deprecating this and instead warn if the user requests external |
168 | | // retpoline thunks and *doesn't* request some form of retpolines. |
169 | 41.3k | auto SpectreOpt = clang::driver::options::ID::OPT_INVALID; |
170 | 41.3k | if (Args.hasArgNoClaim(options::OPT_mretpoline, options::OPT_mno_retpoline, |
171 | 41.3k | options::OPT_mspeculative_load_hardening, |
172 | 41.3k | options::OPT_mno_speculative_load_hardening)) { |
173 | 18 | if (Args.hasFlag(options::OPT_mretpoline, options::OPT_mno_retpoline, |
174 | 18 | false)) { |
175 | 7 | Features.push_back("+retpoline-indirect-calls"); |
176 | 7 | Features.push_back("+retpoline-indirect-branches"); |
177 | 7 | SpectreOpt = options::OPT_mretpoline; |
178 | 11 | } else if (Args.hasFlag(options::OPT_mspeculative_load_hardening, |
179 | 11 | options::OPT_mno_speculative_load_hardening, |
180 | 11 | false)) { |
181 | | // On x86, speculative load hardening relies on at least using retpolines |
182 | | // for indirect calls. |
183 | 5 | Features.push_back("+retpoline-indirect-calls"); |
184 | 5 | SpectreOpt = options::OPT_mspeculative_load_hardening; |
185 | 5 | } |
186 | 41.3k | } else if (Args.hasFlag(options::OPT_mretpoline_external_thunk, |
187 | 41.3k | options::OPT_mno_retpoline_external_thunk, false)) { |
188 | | // FIXME: Add a warning about failing to specify `-mretpoline` and |
189 | | // eventually switch to an error here. |
190 | 3 | Features.push_back("+retpoline-indirect-calls"); |
191 | 3 | Features.push_back("+retpoline-indirect-branches"); |
192 | 3 | SpectreOpt = options::OPT_mretpoline_external_thunk; |
193 | 3 | } |
194 | | |
195 | 41.3k | auto LVIOpt = clang::driver::options::ID::OPT_INVALID; |
196 | 41.3k | if (Args.hasFlag(options::OPT_mlvi_hardening, options::OPT_mno_lvi_hardening, |
197 | 41.3k | false)) { |
198 | 5 | Features.push_back("+lvi-load-hardening"); |
199 | 5 | Features.push_back("+lvi-cfi"); // load hardening implies CFI protection |
200 | 5 | LVIOpt = options::OPT_mlvi_hardening; |
201 | 41.3k | } else if (Args.hasFlag(options::OPT_mlvi_cfi, options::OPT_mno_lvi_cfi, |
202 | 41.3k | false)) { |
203 | 4 | Features.push_back("+lvi-cfi"); |
204 | 4 | LVIOpt = options::OPT_mlvi_cfi; |
205 | 4 | } |
206 | | |
207 | 41.3k | if (Args.hasFlag(options::OPT_m_seses, options::OPT_mno_seses, false)) { |
208 | 6 | if (LVIOpt == options::OPT_mlvi_hardening) |
209 | 1 | D.Diag(diag::err_drv_argument_not_allowed_with) |
210 | 1 | << D.getOpts().getOptionName(options::OPT_mlvi_hardening) |
211 | 1 | << D.getOpts().getOptionName(options::OPT_m_seses); |
212 | | |
213 | 6 | if (SpectreOpt != clang::driver::options::ID::OPT_INVALID) |
214 | 3 | D.Diag(diag::err_drv_argument_not_allowed_with) |
215 | 3 | << D.getOpts().getOptionName(SpectreOpt) |
216 | 3 | << D.getOpts().getOptionName(options::OPT_m_seses); |
217 | | |
218 | 6 | Features.push_back("+seses"); |
219 | 6 | if (!Args.hasArg(options::OPT_mno_lvi_cfi)) { |
220 | 5 | Features.push_back("+lvi-cfi"); |
221 | 5 | LVIOpt = options::OPT_mlvi_cfi; |
222 | 5 | } |
223 | 6 | } |
224 | | |
225 | 41.3k | if (SpectreOpt != clang::driver::options::ID::OPT_INVALID && |
226 | 41.3k | LVIOpt != clang::driver::options::ID::OPT_INVALID15 ) { |
227 | 9 | D.Diag(diag::err_drv_argument_not_allowed_with) |
228 | 9 | << D.getOpts().getOptionName(SpectreOpt) |
229 | 9 | << D.getOpts().getOptionName(LVIOpt); |
230 | 9 | } |
231 | | |
232 | 41.3k | for (const Arg *A : Args.filtered(options::OPT_m_x86_AVX10_Features_Group)) { |
233 | 17 | StringRef Name = A->getOption().getName(); |
234 | 17 | A->claim(); |
235 | | |
236 | | // Skip over "-m". |
237 | 17 | assert(Name.startswith("m") && "Invalid feature name."); |
238 | 17 | Name = Name.substr(1); |
239 | | |
240 | 17 | bool IsNegative = Name.startswith("no-"); |
241 | 17 | if (IsNegative) |
242 | 0 | Name = Name.substr(3); |
243 | | |
244 | 17 | #ifndef NDEBUG |
245 | 17 | assert(Name.startswith("avx10.") && "Invalid AVX10 feature name."); |
246 | 17 | StringRef Version, Width; |
247 | 17 | std::tie(Version, Width) = Name.substr(6).split('-'); |
248 | 17 | assert(Version == "1" && "Invalid AVX10 feature name."); |
249 | 17 | assert((Width == "256" || Width == "512") && "Invalid AVX10 feature name."); |
250 | 17 | #endif |
251 | | |
252 | 17 | Features.push_back(Args.MakeArgString((IsNegative ? "-"0 : "+") + Name)); |
253 | 17 | } |
254 | | |
255 | | // Now add any that the user explicitly requested on the command line, |
256 | | // which may override the defaults. |
257 | 41.3k | for (const Arg *A : Args.filtered(options::OPT_m_x86_Features_Group, |
258 | 41.3k | options::OPT_mgeneral_regs_only)) { |
259 | 578 | StringRef Name = A->getOption().getName(); |
260 | 578 | A->claim(); |
261 | | |
262 | | // Skip over "-m". |
263 | 578 | assert(Name.startswith("m") && "Invalid feature name."); |
264 | 578 | Name = Name.substr(1); |
265 | | |
266 | | // Replace -mgeneral-regs-only with -x87, -mmx, -sse |
267 | 578 | if (A->getOption().getID() == options::OPT_mgeneral_regs_only) { |
268 | 46 | Features.insert(Features.end(), {"-x87", "-mmx", "-sse"}); |
269 | 46 | continue; |
270 | 46 | } |
271 | | |
272 | 532 | bool IsNegative = Name.startswith("no-"); |
273 | 532 | if (IsNegative) |
274 | 215 | Name = Name.substr(3); |
275 | 532 | Features.push_back(Args.MakeArgString((IsNegative ? "-"215 : "+"317 ) + Name)); |
276 | 532 | } |
277 | | |
278 | | // Enable/disable straight line speculation hardening. |
279 | 41.3k | if (Arg *A = Args.getLastArg(options::OPT_mharden_sls_EQ)) { |
280 | 5 | StringRef Scope = A->getValue(); |
281 | 5 | if (Scope == "all") { |
282 | 1 | Features.push_back("+harden-sls-ijmp"); |
283 | 1 | Features.push_back("+harden-sls-ret"); |
284 | 4 | } else if (Scope == "return") { |
285 | 1 | Features.push_back("+harden-sls-ret"); |
286 | 3 | } else if (Scope == "indirect-jmp") { |
287 | 1 | Features.push_back("+harden-sls-ijmp"); |
288 | 2 | } else if (Scope != "none") { |
289 | 1 | D.Diag(diag::err_drv_unsupported_option_argument) |
290 | 1 | << A->getSpelling() << Scope; |
291 | 1 | } |
292 | 5 | } |
293 | | |
294 | | // -mno-gather, -mno-scatter support |
295 | 41.3k | if (Args.hasArg(options::OPT_mno_gather)) |
296 | 2 | Features.push_back("+prefer-no-gather"); |
297 | 41.3k | if (Args.hasArg(options::OPT_mno_scatter)) |
298 | 2 | Features.push_back("+prefer-no-scatter"); |
299 | 41.3k | } |