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