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 
18 using namespace clang::driver;
19 using namespace clang::driver::tools;
20 using namespace clang;
21 using namespace llvm::opt;
22 
23 /// \returns true if the given triple can determine the default CPU type even
24 /// if -arch is not specified.
25 static 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.
32 std::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(options::OPT_mcpu_EQ))) {
37     StringRef Mcpu = A->getValue();
38     CPU = Mcpu.split("+").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+...
74 static bool DecodeAArch64Features(const Driver &D, StringRef text,
75                                   llvm::AArch64::ExtensionSet &Extensions) {
76   SmallVector<StringRef, 8> Split;
77   text.split(Split, StringRef("+"), -1, false);
78 
79   for (StringRef Feature : Split) {
80     if (Feature == "neon" || Feature == "noneon") {
81       D.Diag(clang::diag::err_drv_no_neon_modifier);
82       continue;
83     }
84     if (!Extensions.parseModifier(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.
93 static bool DecodeAArch64Mcpu(const Driver &D, StringRef Mcpu, StringRef &CPU,
94                               llvm::AArch64::ExtensionSet &Extensions) {
95   std::pair<StringRef, StringRef> Split = Mcpu.split("+");
96   CPU = Split.first;
97 
98   if (CPU == "native")
99     CPU = llvm::sys::getHostCPUName();
100 
101   if (CPU == "generic") {
102     Extensions.enable(llvm::AArch64::AEK_SIMD);
103   } else {
104     const std::optional<llvm::AArch64::CpuInfo> CpuInfo =
105         llvm::AArch64::parseCpu(CPU);
106     if (!CpuInfo)
107       return false;
108 
109     Extensions.addCPUDefaults(*CpuInfo);
110   }
111 
112   if (Split.second.size() &&
113       !DecodeAArch64Features(D, Split.second, Extensions))
114     return false;
115 
116   return true;
117 }
118 
119 static bool
120 getAArch64ArchFeaturesFromMarch(const Driver &D, StringRef March,
121                                 const ArgList &Args,
122                                 llvm::AArch64::ExtensionSet &Extensions) {
123   std::string MarchLowerCase = March.lower();
124   std::pair<StringRef, StringRef> Split = StringRef(MarchLowerCase).split("+");
125 
126   const llvm::AArch64::ArchInfo *ArchInfo =
127       llvm::AArch64::parseArch(Split.first);
128   if (Split.first == "native")
129     ArchInfo = llvm::AArch64::getArchForCpu(llvm::sys::getHostCPUName().str());
130   if (!ArchInfo)
131     return false;
132 
133   Extensions.addArchDefaults(*ArchInfo);
134 
135   if ((Split.second.size() &&
136        !DecodeAArch64Features(D, Split.second, Extensions)))
137     return false;
138 
139   return true;
140 }
141 
142 static bool
143 getAArch64ArchFeaturesFromMcpu(const Driver &D, StringRef Mcpu,
144                                const ArgList &Args,
145                                llvm::AArch64::ExtensionSet &Extensions) {
146   StringRef CPU;
147   std::string McpuLowerCase = Mcpu.lower();
148   if (!DecodeAArch64Mcpu(D, McpuLowerCase, CPU, Extensions))
149     return false;
150 
151   return true;
152 }
153 
154 static bool
155 getAArch64MicroArchFeaturesFromMtune(const Driver &D, StringRef Mtune,
156                                      const ArgList &Args,
157                                      std::vector<StringRef> &Features) {
158   std::string MtuneLowerCase = Mtune.lower();
159   // Check CPU name is valid, but ignore any extensions on it.
160   llvm::AArch64::ExtensionSet Extensions;
161   StringRef Tune;
162   if (!DecodeAArch64Mcpu(D, MtuneLowerCase, Tune, Extensions))
163     return false;
164 
165   // Handle CPU name is 'native'.
166   if (MtuneLowerCase == "native")
167     MtuneLowerCase = std::string(llvm::sys::getHostCPUName());
168   if (MtuneLowerCase == "cyclone" ||
169       StringRef(MtuneLowerCase).starts_with("apple")) {
170     Features.push_back("+zcm");
171     Features.push_back("+zcz");
172   }
173   return true;
174 }
175 
176 static bool
177 getAArch64MicroArchFeaturesFromMcpu(const Driver &D, StringRef Mcpu,
178                                     const ArgList &Args,
179                                     std::vector<StringRef> &Features) {
180   StringRef CPU;
181   // Check CPU name is valid, but ignore any extensions on it.
182   llvm::AArch64::ExtensionSet DecodedFeature;
183   std::string McpuLowerCase = Mcpu.lower();
184   if (!DecodeAArch64Mcpu(D, McpuLowerCase, CPU, DecodedFeature))
185     return false;
186 
187   return getAArch64MicroArchFeaturesFromMtune(D, CPU, Args, Features);
188 }
189 
190 void aarch64::getAArch64TargetFeatures(const Driver &D,
191                                        const llvm::Triple &Triple,
192                                        const ArgList &Args,
193                                        std::vector<StringRef> &Features,
194                                        bool ForAS) {
195   Arg *A;
196   bool success = true;
197   llvm::StringRef WaMArch;
198   llvm::AArch64::ExtensionSet Extensions;
199   if (ForAS)
200     for (const auto *A :
201          Args.filtered(options::OPT_Wa_COMMA, options::OPT_Xassembler))
202       for (StringRef Value : A->getValues())
203         if (Value.starts_with("-march="))
204           WaMArch = Value.substr(7);
205   // Call getAArch64ArchFeaturesFromMarch only if "-Wa,-march=" or
206   // "-Xassembler -march" is detected. Otherwise it may return false
207   // and causes Clang to error out.
208   if (!WaMArch.empty())
209     success = getAArch64ArchFeaturesFromMarch(D, WaMArch, Args, Extensions);
210   else if ((A = Args.getLastArg(options::OPT_march_EQ)))
211     success =
212         getAArch64ArchFeaturesFromMarch(D, A->getValue(), Args, Extensions);
213   else if ((A = Args.getLastArg(options::OPT_mcpu_EQ)))
214     success =
215         getAArch64ArchFeaturesFromMcpu(D, A->getValue(), Args, Extensions);
216   else if (isCPUDeterminedByTriple(Triple))
217     success = getAArch64ArchFeaturesFromMcpu(
218         D, getAArch64TargetCPU(Args, Triple, A), Args, Extensions);
219   else
220     // Default to 'A' profile if the architecture is not specified.
221     success = getAArch64ArchFeaturesFromMarch(D, "armv8-a", Args, Extensions);
222 
223   if (success && (A = Args.getLastArg(clang::driver::options::OPT_mtune_EQ)))
224     success =
225         getAArch64MicroArchFeaturesFromMtune(D, A->getValue(), Args, Features);
226   else if (success && (A = Args.getLastArg(options::OPT_mcpu_EQ)))
227     success =
228         getAArch64MicroArchFeaturesFromMcpu(D, A->getValue(), Args, Features);
229   else if (success && isCPUDeterminedByTriple(Triple))
230     success = getAArch64MicroArchFeaturesFromMcpu(
231         D, getAArch64TargetCPU(Args, Triple, A), Args, Features);
232 
233   if (!success) {
234     auto Diag = D.Diag(diag::err_drv_unsupported_option_argument);
235     // If "-Wa,-march=" is used, 'WaMArch' will contain the argument's value,
236     // while 'A' is uninitialized. Only dereference 'A' in the other case.
237     if (!WaMArch.empty())
238       Diag << "-march=" << WaMArch;
239     else
240       Diag << A->getSpelling() << A->getValue();
241   }
242 
243   // -mgeneral-regs-only disables all floating-point features.
244   if (Args.getLastArg(options::OPT_mgeneral_regs_only)) {
245     Extensions.disable(llvm::AArch64::AEK_FP);
246   }
247 
248   // En/disable crc
249   if (Arg *A = Args.getLastArg(options::OPT_mcrc, options::OPT_mnocrc)) {
250     if (A->getOption().matches(options::OPT_mcrc))
251       Extensions.enable(llvm::AArch64::AEK_CRC);
252     else
253       Extensions.disable(llvm::AArch64::AEK_CRC);
254   }
255 
256   // At this point all hardware features are decided, so convert the extensions
257   // set to a feature list.
258   Extensions.toLLVMFeatureList(Features);
259 
260   if (Arg *A = Args.getLastArg(options::OPT_mtp_mode_EQ)) {
261     StringRef Mtp = A->getValue();
262     if (Mtp == "el3" || Mtp == "tpidr_el3")
263       Features.push_back("+tpidr-el3");
264     else if (Mtp == "el2" || Mtp == "tpidr_el2")
265       Features.push_back("+tpidr-el2");
266     else if (Mtp == "el1" || Mtp == "tpidr_el1")
267       Features.push_back("+tpidr-el1");
268     else if (Mtp == "tpidrro_el0")
269       Features.push_back("+tpidrro-el0");
270     else if (Mtp != "el0" && Mtp != "tpidr_el0")
271       D.Diag(diag::err_drv_invalid_mtp) << A->getAsString(Args);
272   }
273 
274   // Enable/disable straight line speculation hardening.
275   if (Arg *A = Args.getLastArg(options::OPT_mharden_sls_EQ)) {
276     StringRef Scope = A->getValue();
277     bool EnableRetBr = false;
278     bool EnableBlr = false;
279     bool DisableComdat = false;
280     if (Scope != "none") {
281       SmallVector<StringRef, 4> Opts;
282       Scope.split(Opts, ",");
283       for (auto Opt : Opts) {
284         Opt = Opt.trim();
285         if (Opt == "all") {
286           EnableBlr = true;
287           EnableRetBr = true;
288           continue;
289         }
290         if (Opt == "retbr") {
291           EnableRetBr = true;
292           continue;
293         }
294         if (Opt == "blr") {
295           EnableBlr = true;
296           continue;
297         }
298         if (Opt == "comdat") {
299           DisableComdat = false;
300           continue;
301         }
302         if (Opt == "nocomdat") {
303           DisableComdat = true;
304           continue;
305         }
306         D.Diag(diag::err_drv_unsupported_option_argument)
307             << A->getSpelling() << Scope;
308         break;
309       }
310     }
311 
312     if (EnableRetBr)
313       Features.push_back("+harden-sls-retbr");
314     if (EnableBlr)
315       Features.push_back("+harden-sls-blr");
316     if (DisableComdat) {
317       Features.push_back("+harden-sls-nocomdat");
318     }
319   }
320 
321   if (Arg *A = Args.getLastArg(options::OPT_mno_unaligned_access,
322                                options::OPT_munaligned_access)) {
323     if (A->getOption().matches(options::OPT_mno_unaligned_access))
324       Features.push_back("+strict-align");
325   } else if (Triple.isOSOpenBSD())
326     Features.push_back("+strict-align");
327 
328   if (Args.hasArg(options::OPT_ffixed_x1))
329     Features.push_back("+reserve-x1");
330 
331   if (Args.hasArg(options::OPT_ffixed_x2))
332     Features.push_back("+reserve-x2");
333 
334   if (Args.hasArg(options::OPT_ffixed_x3))
335     Features.push_back("+reserve-x3");
336 
337   if (Args.hasArg(options::OPT_ffixed_x4))
338     Features.push_back("+reserve-x4");
339 
340   if (Args.hasArg(options::OPT_ffixed_x5))
341     Features.push_back("+reserve-x5");
342 
343   if (Args.hasArg(options::OPT_ffixed_x6))
344     Features.push_back("+reserve-x6");
345 
346   if (Args.hasArg(options::OPT_ffixed_x7))
347     Features.push_back("+reserve-x7");
348 
349   if (Args.hasArg(options::OPT_ffixed_x9))
350     Features.push_back("+reserve-x9");
351 
352   if (Args.hasArg(options::OPT_ffixed_x10))
353     Features.push_back("+reserve-x10");
354 
355   if (Args.hasArg(options::OPT_ffixed_x11))
356     Features.push_back("+reserve-x11");
357 
358   if (Args.hasArg(options::OPT_ffixed_x12))
359     Features.push_back("+reserve-x12");
360 
361   if (Args.hasArg(options::OPT_ffixed_x13))
362     Features.push_back("+reserve-x13");
363 
364   if (Args.hasArg(options::OPT_ffixed_x14))
365     Features.push_back("+reserve-x14");
366 
367   if (Args.hasArg(options::OPT_ffixed_x15))
368     Features.push_back("+reserve-x15");
369 
370   if (Args.hasArg(options::OPT_ffixed_x18))
371     Features.push_back("+reserve-x18");
372 
373   if (Args.hasArg(options::OPT_ffixed_x20))
374     Features.push_back("+reserve-x20");
375 
376   if (Args.hasArg(options::OPT_ffixed_x21))
377     Features.push_back("+reserve-x21");
378 
379   if (Args.hasArg(options::OPT_ffixed_x22))
380     Features.push_back("+reserve-x22");
381 
382   if (Args.hasArg(options::OPT_ffixed_x23))
383     Features.push_back("+reserve-x23");
384 
385   if (Args.hasArg(options::OPT_ffixed_x24))
386     Features.push_back("+reserve-x24");
387 
388   if (Args.hasArg(options::OPT_ffixed_x25))
389     Features.push_back("+reserve-x25");
390 
391   if (Args.hasArg(options::OPT_ffixed_x26))
392     Features.push_back("+reserve-x26");
393 
394   if (Args.hasArg(options::OPT_ffixed_x27))
395     Features.push_back("+reserve-x27");
396 
397   if (Args.hasArg(options::OPT_ffixed_x28))
398     Features.push_back("+reserve-x28");
399 
400   if (Args.hasArg(options::OPT_ffixed_x30))
401     Features.push_back("+reserve-x30");
402 
403   if (Args.hasArg(options::OPT_fcall_saved_x8))
404     Features.push_back("+call-saved-x8");
405 
406   if (Args.hasArg(options::OPT_fcall_saved_x9))
407     Features.push_back("+call-saved-x9");
408 
409   if (Args.hasArg(options::OPT_fcall_saved_x10))
410     Features.push_back("+call-saved-x10");
411 
412   if (Args.hasArg(options::OPT_fcall_saved_x11))
413     Features.push_back("+call-saved-x11");
414 
415   if (Args.hasArg(options::OPT_fcall_saved_x12))
416     Features.push_back("+call-saved-x12");
417 
418   if (Args.hasArg(options::OPT_fcall_saved_x13))
419     Features.push_back("+call-saved-x13");
420 
421   if (Args.hasArg(options::OPT_fcall_saved_x14))
422     Features.push_back("+call-saved-x14");
423 
424   if (Args.hasArg(options::OPT_fcall_saved_x15))
425     Features.push_back("+call-saved-x15");
426 
427   if (Args.hasArg(options::OPT_fcall_saved_x18))
428     Features.push_back("+call-saved-x18");
429 
430   if (Args.hasArg(options::OPT_mno_neg_immediates))
431     Features.push_back("+no-neg-immediates");
432 
433   if (Arg *A = Args.getLastArg(options::OPT_mfix_cortex_a53_835769,
434                                options::OPT_mno_fix_cortex_a53_835769)) {
435     if (A->getOption().matches(options::OPT_mfix_cortex_a53_835769))
436       Features.push_back("+fix-cortex-a53-835769");
437     else
438       Features.push_back("-fix-cortex-a53-835769");
439   } else if (Triple.isAndroid() || Triple.isOHOSFamily()) {
440     // Enabled A53 errata (835769) workaround by default on android
441     Features.push_back("+fix-cortex-a53-835769");
442   } else if (Triple.isOSFuchsia()) {
443     std::string CPU = getCPUName(D, Args, Triple);
444     if (CPU.empty() || CPU == "generic" || CPU == "cortex-a53")
445       Features.push_back("+fix-cortex-a53-835769");
446   }
447 
448   if (Args.getLastArg(options::OPT_mno_bti_at_return_twice))
449     Features.push_back("+no-bti-at-return-twice");
450 }
451