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