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

source code of clang/lib/Driver/ToolChains/Arch/AArch64.cpp