1//===--- AArch64.cpp - AArch64 (not ARM) 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 "AArch64.h"
10#include "../CommonArgs.h"
11#include "clang/Driver/Driver.h"
12#include "clang/Driver/DriverDiagnostic.h"
13#include "clang/Driver/Options.h"
14#include "llvm/Option/ArgList.h"
15#include "llvm/TargetParser/AArch64TargetParser.h"
16#include "llvm/TargetParser/Host.h"
17
18using namespace clang::driver;
19using namespace clang::driver::tools;
20using namespace clang;
21using namespace llvm::opt;
22
23/// \returns true if the given triple can determine the default CPU type even
24/// if -arch is not specified.
25static bool isCPUDeterminedByTriple(const llvm::Triple &Triple) {
26 return Triple.isOSDarwin();
27}
28
29/// getAArch64TargetCPU - Get the (LLVM) name of the AArch64 cpu we are
30/// targeting. Set \p A to the Arg corresponding to the -mcpu argument if it is
31/// provided, or to nullptr otherwise.
32std::string aarch64::getAArch64TargetCPU(const ArgList &Args,
33 const llvm::Triple &Triple, Arg *&A) {
34 std::string CPU;
35 // If we have -mcpu, use that.
36 if ((A = Args.getLastArg(Ids: options::OPT_mcpu_EQ))) {
37 StringRef Mcpu = A->getValue();
38 CPU = Mcpu.split(Separator: "+").first.lower();
39 }
40
41 CPU = llvm::AArch64::resolveCPUAlias(CPU);
42
43 // Handle CPU name is 'native'.
44 if (CPU == "native")
45 return std::string(llvm::sys::getHostCPUName());
46
47 if (CPU.size())
48 return CPU;
49
50 if (Triple.isTargetMachineMac() &&
51 Triple.getArch() == llvm::Triple::aarch64) {
52 // Apple Silicon macs default to M1 CPUs.
53 return "apple-m1";
54 }
55
56 if (Triple.isXROS()) {
57 // The xrOS simulator runs on M1 as well, it should have been covered above.
58 assert(!Triple.isSimulatorEnvironment() && "xrossim should be mac-like");
59 return "apple-a12";
60 }
61 // arm64e requires v8.3a and only runs on apple-a12 and later CPUs.
62 if (Triple.isArm64e())
63 return "apple-a12";
64
65 // Make sure we pick the appropriate Apple CPU when targetting a Darwin OS.
66 if (Triple.isOSDarwin())
67 return Triple.getArch() == llvm::Triple::aarch64_32 ? "apple-s4"
68 : "apple-a7";
69
70 return "generic";
71}
72
73// Decode AArch64 features from string like +[no]featureA+[no]featureB+...
74static bool DecodeAArch64Features(const Driver &D, StringRef text,
75 llvm::AArch64::ExtensionSet &Extensions) {
76 SmallVector<StringRef, 8> Split;
77 text.split(A&: Split, Separator: StringRef("+"), MaxSplit: -1, KeepEmpty: false);
78
79 for (StringRef Feature : Split) {
80 if (Feature == "neon" || Feature == "noneon") {
81 D.Diag(DiagID: clang::diag::err_drv_no_neon_modifier);
82 continue;
83 }
84 if (!Extensions.parseModifier(Modifier: Feature))
85 return false;
86 }
87
88 return true;
89}
90
91// Check if the CPU name and feature modifiers in -mcpu are legal. If yes,
92// decode CPU and feature.
93static bool DecodeAArch64Mcpu(const Driver &D, StringRef Mcpu, StringRef &CPU,
94 llvm::AArch64::ExtensionSet &Extensions) {
95 std::pair<StringRef, StringRef> Split = Mcpu.split(Separator: "+");
96 CPU = Split.first;
97
98 if (CPU == "native")
99 CPU = llvm::sys::getHostCPUName();
100
101 const std::optional<llvm::AArch64::CpuInfo> CpuInfo =
102 llvm::AArch64::parseCpu(Name: CPU);
103 if (!CpuInfo)
104 return false;
105
106 Extensions.addCPUDefaults(CPU: *CpuInfo);
107
108 if (Split.second.size() &&
109 !DecodeAArch64Features(D, text: Split.second, Extensions))
110 return false;
111
112 return true;
113}
114
115static bool
116getAArch64ArchFeaturesFromMarch(const Driver &D, StringRef March,
117 const ArgList &Args,
118 llvm::AArch64::ExtensionSet &Extensions) {
119 std::string MarchLowerCase = March.lower();
120 std::pair<StringRef, StringRef> Split = StringRef(MarchLowerCase).split(Separator: "+");
121
122 const llvm::AArch64::ArchInfo *ArchInfo =
123 llvm::AArch64::parseArch(Arch: Split.first);
124 if (Split.first == "native")
125 ArchInfo = llvm::AArch64::getArchForCpu(CPU: llvm::sys::getHostCPUName().str());
126 if (!ArchInfo)
127 return false;
128
129 Extensions.addArchDefaults(Arch: *ArchInfo);
130
131 if ((Split.second.size() &&
132 !DecodeAArch64Features(D, text: Split.second, Extensions)))
133 return false;
134
135 return true;
136}
137
138static bool
139getAArch64ArchFeaturesFromMcpu(const Driver &D, StringRef Mcpu,
140 const ArgList &Args,
141 llvm::AArch64::ExtensionSet &Extensions) {
142 StringRef CPU;
143 std::string McpuLowerCase = Mcpu.lower();
144 if (!DecodeAArch64Mcpu(D, Mcpu: McpuLowerCase, CPU, Extensions))
145 return false;
146
147 return true;
148}
149
150static bool
151getAArch64MicroArchFeaturesFromMtune(const Driver &D, StringRef Mtune,
152 const ArgList &Args,
153 std::vector<StringRef> &Features) {
154 std::string MtuneLowerCase = Mtune.lower();
155 // Check CPU name is valid, but ignore any extensions on it.
156 llvm::AArch64::ExtensionSet Extensions;
157 StringRef Tune;
158 if (!DecodeAArch64Mcpu(D, Mcpu: MtuneLowerCase, CPU&: Tune, Extensions))
159 return false;
160
161 // Handle CPU name is 'native'.
162 if (MtuneLowerCase == "native")
163 MtuneLowerCase = std::string(llvm::sys::getHostCPUName());
164
165 // 'cyclone' and later have zero-cycle register moves and zeroing.
166 if (MtuneLowerCase == "cyclone" ||
167 StringRef(MtuneLowerCase).starts_with(Prefix: "apple")) {
168 Features.push_back(x: "+zcm");
169 Features.push_back(x: "+zcz");
170 }
171
172 return true;
173}
174
175static bool
176getAArch64MicroArchFeaturesFromMcpu(const Driver &D, StringRef Mcpu,
177 const ArgList &Args,
178 std::vector<StringRef> &Features) {
179 StringRef CPU;
180 // Check CPU name is valid, but ignore any extensions on it.
181 llvm::AArch64::ExtensionSet DecodedFeature;
182 std::string McpuLowerCase = Mcpu.lower();
183 if (!DecodeAArch64Mcpu(D, Mcpu: McpuLowerCase, CPU, Extensions&: DecodedFeature))
184 return false;
185
186 return getAArch64MicroArchFeaturesFromMtune(D, Mtune: CPU, Args, Features);
187}
188
189void aarch64::getAArch64TargetFeatures(const Driver &D,
190 const llvm::Triple &Triple,
191 const ArgList &Args,
192 std::vector<StringRef> &Features,
193 bool ForAS) {
194 Arg *A;
195 bool success = true;
196 llvm::StringRef WaMArch;
197 llvm::AArch64::ExtensionSet Extensions;
198 if (ForAS)
199 for (const auto *A :
200 Args.filtered(Ids: options::OPT_Wa_COMMA, Ids: options::OPT_Xassembler))
201 for (StringRef Value : A->getValues())
202 if (Value.starts_with(Prefix: "-march="))
203 WaMArch = Value.substr(Start: 7);
204 // Call getAArch64ArchFeaturesFromMarch only if "-Wa,-march=" or
205 // "-Xassembler -march" is detected. Otherwise it may return false
206 // and causes Clang to error out.
207 if (!WaMArch.empty())
208 success = getAArch64ArchFeaturesFromMarch(D, March: WaMArch, Args, Extensions);
209 else if ((A = Args.getLastArg(Ids: options::OPT_march_EQ)))
210 success =
211 getAArch64ArchFeaturesFromMarch(D, March: A->getValue(), Args, Extensions);
212 else if ((A = Args.getLastArg(Ids: options::OPT_mcpu_EQ)))
213 success =
214 getAArch64ArchFeaturesFromMcpu(D, Mcpu: A->getValue(), Args, Extensions);
215 else if (isCPUDeterminedByTriple(Triple))
216 success = getAArch64ArchFeaturesFromMcpu(
217 D, Mcpu: getAArch64TargetCPU(Args, Triple, A), Args, Extensions);
218 else
219 // Default to 'A' profile if the architecture is not specified.
220 success = getAArch64ArchFeaturesFromMarch(D, March: "armv8-a", Args, Extensions);
221
222 if (success && (A = Args.getLastArg(Ids: clang::driver::options::OPT_mtune_EQ)))
223 success =
224 getAArch64MicroArchFeaturesFromMtune(D, Mtune: A->getValue(), Args, Features);
225 else if (success && (A = Args.getLastArg(Ids: options::OPT_mcpu_EQ)))
226 success =
227 getAArch64MicroArchFeaturesFromMcpu(D, Mcpu: A->getValue(), Args, Features);
228 else if (success && isCPUDeterminedByTriple(Triple))
229 success = getAArch64MicroArchFeaturesFromMcpu(
230 D, Mcpu: getAArch64TargetCPU(Args, Triple, A), Args, Features);
231
232 if (!success) {
233 auto Diag = D.Diag(DiagID: diag::err_drv_unsupported_option_argument);
234 // If "-Wa,-march=" is used, 'WaMArch' will contain the argument's value,
235 // while 'A' is uninitialized. Only dereference 'A' in the other case.
236 if (!WaMArch.empty())
237 Diag << "-march=" << WaMArch;
238 else
239 Diag << A->getSpelling() << A->getValue();
240 }
241
242 // -mgeneral-regs-only disables all floating-point features.
243 if (Args.getLastArg(Ids: options::OPT_mgeneral_regs_only)) {
244 Extensions.disable(E: llvm::AArch64::AEK_FP);
245 }
246
247 // En/disable crc
248 if (Arg *A = Args.getLastArg(Ids: options::OPT_mcrc, Ids: options::OPT_mnocrc)) {
249 if (A->getOption().matches(ID: options::OPT_mcrc))
250 Extensions.enable(E: llvm::AArch64::AEK_CRC);
251 else
252 Extensions.disable(E: llvm::AArch64::AEK_CRC);
253 }
254
255 // At this point all hardware features are decided, so convert the extensions
256 // set to a feature list.
257 Extensions.toLLVMFeatureList(Features);
258
259 if (Arg *A = Args.getLastArg(Ids: options::OPT_mtp_mode_EQ)) {
260 StringRef Mtp = A->getValue();
261 if (Mtp == "el3" || Mtp == "tpidr_el3")
262 Features.push_back(x: "+tpidr-el3");
263 else if (Mtp == "el2" || Mtp == "tpidr_el2")
264 Features.push_back(x: "+tpidr-el2");
265 else if (Mtp == "el1" || Mtp == "tpidr_el1")
266 Features.push_back(x: "+tpidr-el1");
267 else if (Mtp == "tpidrro_el0")
268 Features.push_back(x: "+tpidrro-el0");
269 else if (Mtp != "el0" && Mtp != "tpidr_el0")
270 D.Diag(DiagID: diag::err_drv_invalid_mtp) << A->getAsString(Args);
271 }
272
273 // Enable/disable straight line speculation hardening.
274 if (Arg *A = Args.getLastArg(Ids: options::OPT_mharden_sls_EQ)) {
275 StringRef Scope = A->getValue();
276 bool EnableRetBr = false;
277 bool EnableBlr = false;
278 bool DisableComdat = false;
279 if (Scope != "none") {
280 SmallVector<StringRef, 4> Opts;
281 Scope.split(A&: Opts, Separator: ",");
282 for (auto Opt : Opts) {
283 Opt = Opt.trim();
284 if (Opt == "all") {
285 EnableBlr = true;
286 EnableRetBr = true;
287 continue;
288 }
289 if (Opt == "retbr") {
290 EnableRetBr = true;
291 continue;
292 }
293 if (Opt == "blr") {
294 EnableBlr = true;
295 continue;
296 }
297 if (Opt == "comdat") {
298 DisableComdat = false;
299 continue;
300 }
301 if (Opt == "nocomdat") {
302 DisableComdat = true;
303 continue;
304 }
305 D.Diag(DiagID: diag::err_drv_unsupported_option_argument)
306 << A->getSpelling() << Scope;
307 break;
308 }
309 }
310
311 if (EnableRetBr)
312 Features.push_back(x: "+harden-sls-retbr");
313 if (EnableBlr)
314 Features.push_back(x: "+harden-sls-blr");
315 if (DisableComdat) {
316 Features.push_back(x: "+harden-sls-nocomdat");
317 }
318 }
319
320 if (Arg *A = Args.getLastArg(
321 Ids: options::OPT_mstrict_align, Ids: options::OPT_mno_strict_align,
322 Ids: options::OPT_mno_unaligned_access, Ids: options::OPT_munaligned_access)) {
323 if (A->getOption().matches(ID: options::OPT_mstrict_align) ||
324 A->getOption().matches(ID: options::OPT_mno_unaligned_access))
325 Features.push_back(x: "+strict-align");
326 } else if (Triple.isOSOpenBSD())
327 Features.push_back(x: "+strict-align");
328
329 if (Args.hasArg(Ids: options::OPT_ffixed_x1))
330 Features.push_back(x: "+reserve-x1");
331
332 if (Args.hasArg(Ids: options::OPT_ffixed_x2))
333 Features.push_back(x: "+reserve-x2");
334
335 if (Args.hasArg(Ids: options::OPT_ffixed_x3))
336 Features.push_back(x: "+reserve-x3");
337
338 if (Args.hasArg(Ids: options::OPT_ffixed_x4))
339 Features.push_back(x: "+reserve-x4");
340
341 if (Args.hasArg(Ids: options::OPT_ffixed_x5))
342 Features.push_back(x: "+reserve-x5");
343
344 if (Args.hasArg(Ids: options::OPT_ffixed_x6))
345 Features.push_back(x: "+reserve-x6");
346
347 if (Args.hasArg(Ids: options::OPT_ffixed_x7))
348 Features.push_back(x: "+reserve-x7");
349
350 if (Args.hasArg(Ids: options::OPT_ffixed_x9))
351 Features.push_back(x: "+reserve-x9");
352
353 if (Args.hasArg(Ids: options::OPT_ffixed_x10))
354 Features.push_back(x: "+reserve-x10");
355
356 if (Args.hasArg(Ids: options::OPT_ffixed_x11))
357 Features.push_back(x: "+reserve-x11");
358
359 if (Args.hasArg(Ids: options::OPT_ffixed_x12))
360 Features.push_back(x: "+reserve-x12");
361
362 if (Args.hasArg(Ids: options::OPT_ffixed_x13))
363 Features.push_back(x: "+reserve-x13");
364
365 if (Args.hasArg(Ids: options::OPT_ffixed_x14))
366 Features.push_back(x: "+reserve-x14");
367
368 if (Args.hasArg(Ids: options::OPT_ffixed_x15))
369 Features.push_back(x: "+reserve-x15");
370
371 if (Args.hasArg(Ids: options::OPT_ffixed_x18))
372 Features.push_back(x: "+reserve-x18");
373
374 if (Args.hasArg(Ids: options::OPT_ffixed_x20))
375 Features.push_back(x: "+reserve-x20");
376
377 if (Args.hasArg(Ids: options::OPT_ffixed_x21))
378 Features.push_back(x: "+reserve-x21");
379
380 if (Args.hasArg(Ids: options::OPT_ffixed_x22))
381 Features.push_back(x: "+reserve-x22");
382
383 if (Args.hasArg(Ids: options::OPT_ffixed_x23))
384 Features.push_back(x: "+reserve-x23");
385
386 if (Args.hasArg(Ids: options::OPT_ffixed_x24))
387 Features.push_back(x: "+reserve-x24");
388
389 if (Args.hasArg(Ids: options::OPT_ffixed_x25))
390 Features.push_back(x: "+reserve-x25");
391
392 if (Args.hasArg(Ids: options::OPT_ffixed_x26))
393 Features.push_back(x: "+reserve-x26");
394
395 if (Args.hasArg(Ids: options::OPT_ffixed_x27))
396 Features.push_back(x: "+reserve-x27");
397
398 if (Args.hasArg(Ids: options::OPT_ffixed_x28))
399 Features.push_back(x: "+reserve-x28");
400
401 if (Args.hasArg(Ids: options::OPT_mlr_for_calls_only))
402 Features.push_back(x: "+reserve-lr-for-ra");
403
404 if (Args.hasArg(Ids: options::OPT_fcall_saved_x8))
405 Features.push_back(x: "+call-saved-x8");
406
407 if (Args.hasArg(Ids: options::OPT_fcall_saved_x9))
408 Features.push_back(x: "+call-saved-x9");
409
410 if (Args.hasArg(Ids: options::OPT_fcall_saved_x10))
411 Features.push_back(x: "+call-saved-x10");
412
413 if (Args.hasArg(Ids: options::OPT_fcall_saved_x11))
414 Features.push_back(x: "+call-saved-x11");
415
416 if (Args.hasArg(Ids: options::OPT_fcall_saved_x12))
417 Features.push_back(x: "+call-saved-x12");
418
419 if (Args.hasArg(Ids: options::OPT_fcall_saved_x13))
420 Features.push_back(x: "+call-saved-x13");
421
422 if (Args.hasArg(Ids: options::OPT_fcall_saved_x14))
423 Features.push_back(x: "+call-saved-x14");
424
425 if (Args.hasArg(Ids: options::OPT_fcall_saved_x15))
426 Features.push_back(x: "+call-saved-x15");
427
428 if (Args.hasArg(Ids: options::OPT_fcall_saved_x18))
429 Features.push_back(x: "+call-saved-x18");
430
431 if (Args.hasArg(Ids: options::OPT_mno_neg_immediates))
432 Features.push_back(x: "+no-neg-immediates");
433
434 if (Arg *A = Args.getLastArg(Ids: options::OPT_mfix_cortex_a53_835769,
435 Ids: options::OPT_mno_fix_cortex_a53_835769)) {
436 if (A->getOption().matches(ID: options::OPT_mfix_cortex_a53_835769))
437 Features.push_back(x: "+fix-cortex-a53-835769");
438 else
439 Features.push_back(x: "-fix-cortex-a53-835769");
440 } else if (Triple.isAndroid() || Triple.isOHOSFamily()) {
441 // Enabled A53 errata (835769) workaround by default on android
442 Features.push_back(x: "+fix-cortex-a53-835769");
443 } else if (Triple.isOSFuchsia()) {
444 std::string CPU = getCPUName(D, Args, T: Triple);
445 if (CPU.empty() || CPU == "generic" || CPU == "cortex-a53")
446 Features.push_back(x: "+fix-cortex-a53-835769");
447 }
448
449 if (Args.getLastArg(Ids: options::OPT_mno_bti_at_return_twice))
450 Features.push_back(x: "+no-bti-at-return-twice");
451}
452
453void aarch64::setPAuthABIInTriple(const Driver &D, const ArgList &Args,
454 llvm::Triple &Triple) {
455 Arg *ABIArg = Args.getLastArg(Ids: options::OPT_mabi_EQ);
456 bool HasPAuthABI =
457 ABIArg ? (StringRef(ABIArg->getValue()) == "pauthtest") : false;
458
459 switch (Triple.getEnvironment()) {
460 case llvm::Triple::UnknownEnvironment:
461 if (HasPAuthABI)
462 Triple.setEnvironment(llvm::Triple::PAuthTest);
463 break;
464 case llvm::Triple::PAuthTest:
465 break;
466 default:
467 if (HasPAuthABI)
468 D.Diag(DiagID: diag::err_drv_unsupported_opt_for_target)
469 << ABIArg->getAsString(Args) << Triple.getTriple();
470 break;
471 }
472}
473