xref: /freebsd/contrib/llvm-project/clang/lib/Driver/ToolChains/Arch/AArch64.cpp (revision 5e801ac66d24704442eba426ed13c3effb8a34e7)
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/Driver.h"
11 #include "clang/Driver/DriverDiagnostic.h"
12 #include "clang/Driver/Options.h"
13 #include "llvm/Option/ArgList.h"
14 #include "llvm/Support/TargetParser.h"
15 #include "llvm/Support/Host.h"
16 
17 using namespace clang::driver;
18 using namespace clang::driver::tools;
19 using namespace clang;
20 using namespace llvm::opt;
21 
22 /// \returns true if the given triple can determine the default CPU type even
23 /// if -arch is not specified.
24 static 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.
31 std::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(options::OPT_mcpu_EQ))) {
36     StringRef Mcpu = A->getValue();
37     CPU = Mcpu.split("+").first.lower();
38   }
39 
40   // Handle CPU name is 'native'.
41   if (CPU == "native")
42     return std::string(llvm::sys::getHostCPUName());
43 
44   if (CPU.size())
45     return CPU;
46 
47   if (Triple.isTargetMachineMac() &&
48       Triple.getArch() == llvm::Triple::aarch64) {
49     // Apple Silicon macs default to M1 CPUs.
50     return "apple-m1";
51   }
52 
53   // arm64e requires v8.3a and only runs on apple-a12 and later CPUs.
54   if (Triple.isArm64e())
55     return "apple-a12";
56 
57   // Make sure we pick the appropriate Apple CPU if -arch is used or when
58   // targetting a Darwin OS.
59   if (Args.getLastArg(options::OPT_arch) || Triple.isOSDarwin())
60     return Triple.getArch() == llvm::Triple::aarch64_32 ? "apple-s4"
61                                                         : "apple-a7";
62 
63   return "generic";
64 }
65 
66 // Decode AArch64 features from string like +[no]featureA+[no]featureB+...
67 static bool DecodeAArch64Features(const Driver &D, StringRef text,
68                                   std::vector<StringRef> &Features,
69                                   llvm::AArch64::ArchKind ArchKind) {
70   SmallVector<StringRef, 8> Split;
71   text.split(Split, StringRef("+"), -1, false);
72 
73   for (StringRef Feature : Split) {
74     StringRef FeatureName = llvm::AArch64::getArchExtFeature(Feature);
75     if (!FeatureName.empty())
76       Features.push_back(FeatureName);
77     else if (Feature == "neon" || Feature == "noneon")
78       D.Diag(clang::diag::err_drv_no_neon_modifier);
79     else
80       return false;
81 
82     if (Feature == "sve2")
83       Features.push_back("+sve");
84     else if (Feature == "sve2-bitperm" || Feature == "sve2-sha3" ||
85              Feature == "sve2-aes" || Feature == "sve2-sm4") {
86       Features.push_back("+sve");
87       Features.push_back("+sve2");
88     } else if (Feature == "nosve") {
89       Features.push_back("-sve2");
90       Features.push_back("-sve2-bitperm");
91       Features.push_back("-sve2-sha3");
92       Features.push_back("-sve2-aes");
93       Features.push_back("-sve2-sm4");
94     } else if (Feature == "nosve2") {
95       Features.push_back("-sve2-bitperm");
96       Features.push_back("-sve2-sha3");
97       Features.push_back("-sve2-aes");
98       Features.push_back("-sve2-sm4");
99     }
100 
101     // +sve implies +f32mm if the base architecture is v8.6A, v8.7A, v9.1A or
102     // v9.2A. It isn't the case in general that sve implies both f64mm and f32mm
103     if ((ArchKind == llvm::AArch64::ArchKind::ARMV8_6A ||
104          ArchKind == llvm::AArch64::ArchKind::ARMV8_7A ||
105          ArchKind == llvm::AArch64::ArchKind::ARMV9_1A ||
106          ArchKind == llvm::AArch64::ArchKind::ARMV9_2A) &&
107         Feature == "sve")
108       Features.push_back("+f32mm");
109   }
110   return true;
111 }
112 
113 // Check if the CPU name and feature modifiers in -mcpu are legal. If yes,
114 // decode CPU and feature.
115 static bool DecodeAArch64Mcpu(const Driver &D, StringRef Mcpu, StringRef &CPU,
116                               std::vector<StringRef> &Features) {
117   std::pair<StringRef, StringRef> Split = Mcpu.split("+");
118   CPU = Split.first;
119   llvm::AArch64::ArchKind ArchKind = llvm::AArch64::ArchKind::ARMV8A;
120 
121   if (CPU == "native")
122     CPU = llvm::sys::getHostCPUName();
123 
124   if (CPU == "generic") {
125     Features.push_back("+neon");
126   } else {
127     ArchKind = llvm::AArch64::parseCPUArch(CPU);
128     if (!llvm::AArch64::getArchFeatures(ArchKind, Features))
129       return false;
130 
131     uint64_t Extension = llvm::AArch64::getDefaultExtensions(CPU, ArchKind);
132     if (!llvm::AArch64::getExtensionFeatures(Extension, Features))
133       return false;
134    }
135 
136    if (Split.second.size() &&
137        !DecodeAArch64Features(D, Split.second, Features, ArchKind))
138      return false;
139 
140    return true;
141 }
142 
143 static bool
144 getAArch64ArchFeaturesFromMarch(const Driver &D, StringRef March,
145                                 const ArgList &Args,
146                                 std::vector<StringRef> &Features) {
147   std::string MarchLowerCase = March.lower();
148   std::pair<StringRef, StringRef> Split = StringRef(MarchLowerCase).split("+");
149 
150   llvm::AArch64::ArchKind ArchKind = llvm::AArch64::parseArch(Split.first);
151   if (ArchKind == llvm::AArch64::ArchKind::INVALID ||
152       !llvm::AArch64::getArchFeatures(ArchKind, Features))
153     return false;
154 
155   // Enable SVE2 by default on Armv9-A.
156   // It can still be disabled if +nosve2 is present.
157   // We must do this early so that DecodeAArch64Features has the correct state
158   if ((ArchKind == llvm::AArch64::ArchKind::ARMV9A ||
159        ArchKind == llvm::AArch64::ArchKind::ARMV9_1A ||
160        ArchKind == llvm::AArch64::ArchKind::ARMV9_2A)) {
161     Features.push_back("+sve");
162     Features.push_back("+sve2");
163   }
164 
165   if ((Split.second.size() &&
166        !DecodeAArch64Features(D, Split.second, Features, ArchKind)))
167     return false;
168 
169   return true;
170 }
171 
172 static bool
173 getAArch64ArchFeaturesFromMcpu(const Driver &D, StringRef Mcpu,
174                                const ArgList &Args,
175                                std::vector<StringRef> &Features) {
176   StringRef CPU;
177   std::string McpuLowerCase = Mcpu.lower();
178   if (!DecodeAArch64Mcpu(D, McpuLowerCase, CPU, Features))
179     return false;
180 
181   return true;
182 }
183 
184 static bool
185 getAArch64MicroArchFeaturesFromMtune(const Driver &D, StringRef Mtune,
186                                      const ArgList &Args,
187                                      std::vector<StringRef> &Features) {
188   std::string MtuneLowerCase = Mtune.lower();
189   // Check CPU name is valid
190   std::vector<StringRef> MtuneFeatures;
191   StringRef Tune;
192   if (!DecodeAArch64Mcpu(D, MtuneLowerCase, Tune, MtuneFeatures))
193     return false;
194 
195   // Handle CPU name is 'native'.
196   if (MtuneLowerCase == "native")
197     MtuneLowerCase = std::string(llvm::sys::getHostCPUName());
198   if (MtuneLowerCase == "cyclone" ||
199       StringRef(MtuneLowerCase).startswith("apple")) {
200     Features.push_back("+zcm");
201     Features.push_back("+zcz");
202   }
203   return true;
204 }
205 
206 static bool
207 getAArch64MicroArchFeaturesFromMcpu(const Driver &D, StringRef Mcpu,
208                                     const ArgList &Args,
209                                     std::vector<StringRef> &Features) {
210   StringRef CPU;
211   std::vector<StringRef> DecodedFeature;
212   std::string McpuLowerCase = Mcpu.lower();
213   if (!DecodeAArch64Mcpu(D, McpuLowerCase, CPU, DecodedFeature))
214     return false;
215 
216   return getAArch64MicroArchFeaturesFromMtune(D, CPU, Args, Features);
217 }
218 
219 void aarch64::getAArch64TargetFeatures(const Driver &D,
220                                        const llvm::Triple &Triple,
221                                        const ArgList &Args,
222                                        std::vector<StringRef> &Features,
223                                        bool ForAS) {
224   Arg *A;
225   bool success = true;
226   // Enable NEON by default.
227   Features.push_back("+neon");
228   llvm::StringRef WaMArch;
229   if (ForAS)
230     for (const auto *A :
231          Args.filtered(options::OPT_Wa_COMMA, options::OPT_Xassembler))
232       for (StringRef Value : A->getValues())
233         if (Value.startswith("-march="))
234           WaMArch = Value.substr(7);
235   // Call getAArch64ArchFeaturesFromMarch only if "-Wa,-march=" or
236   // "-Xassembler -march" is detected. Otherwise it may return false
237   // and causes Clang to error out.
238   if (!WaMArch.empty())
239     success = getAArch64ArchFeaturesFromMarch(D, WaMArch, Args, Features);
240   else if ((A = Args.getLastArg(options::OPT_march_EQ)))
241     success = getAArch64ArchFeaturesFromMarch(D, A->getValue(), Args, Features);
242   else if ((A = Args.getLastArg(options::OPT_mcpu_EQ)))
243     success = getAArch64ArchFeaturesFromMcpu(D, A->getValue(), Args, Features);
244   else if (Args.hasArg(options::OPT_arch) || isCPUDeterminedByTriple(Triple))
245     success = getAArch64ArchFeaturesFromMcpu(
246         D, getAArch64TargetCPU(Args, Triple, A), Args, Features);
247   else
248     // Default to 'A' profile if the architecture is not specified.
249     success = getAArch64ArchFeaturesFromMarch(D, "armv8-a", Args, Features);
250 
251   if (success && (A = Args.getLastArg(clang::driver::options::OPT_mtune_EQ)))
252     success =
253         getAArch64MicroArchFeaturesFromMtune(D, A->getValue(), Args, Features);
254   else if (success && (A = Args.getLastArg(options::OPT_mcpu_EQ)))
255     success =
256         getAArch64MicroArchFeaturesFromMcpu(D, A->getValue(), Args, Features);
257   else if (success &&
258            (Args.hasArg(options::OPT_arch) || isCPUDeterminedByTriple(Triple)))
259     success = getAArch64MicroArchFeaturesFromMcpu(
260         D, getAArch64TargetCPU(Args, Triple, A), Args, Features);
261 
262   if (!success) {
263     auto Diag = D.Diag(diag::err_drv_clang_unsupported);
264     // If "-Wa,-march=" is used, 'WaMArch' will contain the argument's value,
265     // while 'A' is uninitialized. Only dereference 'A' in the other case.
266     if (!WaMArch.empty())
267       Diag << "-march=" + WaMArch.str();
268     else
269       Diag << A->getAsString(Args);
270   }
271 
272   if (Args.getLastArg(options::OPT_mgeneral_regs_only)) {
273     Features.push_back("-fp-armv8");
274     Features.push_back("-crypto");
275     Features.push_back("-neon");
276   }
277 
278   if (Arg *A = Args.getLastArg(options::OPT_mtp_mode_EQ)) {
279     StringRef Mtp = A->getValue();
280     if (Mtp == "el3")
281       Features.push_back("+tpidr-el3");
282     else if (Mtp == "el2")
283       Features.push_back("+tpidr-el2");
284     else if (Mtp == "el1")
285       Features.push_back("+tpidr-el1");
286     else if (Mtp != "el0")
287       D.Diag(diag::err_drv_invalid_mtp) << A->getAsString(Args);
288   }
289 
290   // Enable/disable straight line speculation hardening.
291   if (Arg *A = Args.getLastArg(options::OPT_mharden_sls_EQ)) {
292     StringRef Scope = A->getValue();
293     bool EnableRetBr = false;
294     bool EnableBlr = false;
295     bool DisableComdat = false;
296     if (Scope != "none") {
297       SmallVector<StringRef, 4> Opts;
298       Scope.split(Opts, ",");
299       for (auto Opt : Opts) {
300         Opt = Opt.trim();
301         if (Opt == "all") {
302           EnableBlr = true;
303           EnableRetBr = true;
304           continue;
305         }
306         if (Opt == "retbr") {
307           EnableRetBr = true;
308           continue;
309         }
310         if (Opt == "blr") {
311           EnableBlr = true;
312           continue;
313         }
314         if (Opt == "comdat") {
315           DisableComdat = false;
316           continue;
317         }
318         if (Opt == "nocomdat") {
319           DisableComdat = true;
320           continue;
321         }
322         D.Diag(diag::err_invalid_sls_hardening)
323             << Scope << A->getAsString(Args);
324         break;
325       }
326     }
327 
328     if (EnableRetBr)
329       Features.push_back("+harden-sls-retbr");
330     if (EnableBlr)
331       Features.push_back("+harden-sls-blr");
332     if (DisableComdat) {
333       Features.push_back("+harden-sls-nocomdat");
334     }
335   }
336 
337   // En/disable crc
338   if (Arg *A = Args.getLastArg(options::OPT_mcrc, options::OPT_mnocrc)) {
339     if (A->getOption().matches(options::OPT_mcrc))
340       Features.push_back("+crc");
341     else
342       Features.push_back("-crc");
343   }
344 
345   // Handle (arch-dependent) fp16fml/fullfp16 relationship.
346   // FIXME: this fp16fml option handling will be reimplemented after the
347   // TargetParser rewrite.
348   const auto ItRNoFullFP16 = std::find(Features.rbegin(), Features.rend(), "-fullfp16");
349   const auto ItRFP16FML = std::find(Features.rbegin(), Features.rend(), "+fp16fml");
350   if (llvm::is_contained(Features, "+v8.4a")) {
351     const auto ItRFullFP16  = std::find(Features.rbegin(), Features.rend(), "+fullfp16");
352     if (ItRFullFP16 < ItRNoFullFP16 && ItRFullFP16 < ItRFP16FML) {
353       // Only entangled feature that can be to the right of this +fullfp16 is -fp16fml.
354       // Only append the +fp16fml if there is no -fp16fml after the +fullfp16.
355       if (std::find(Features.rbegin(), ItRFullFP16, "-fp16fml") == ItRFullFP16)
356         Features.push_back("+fp16fml");
357     }
358     else
359       goto fp16_fml_fallthrough;
360   } else {
361 fp16_fml_fallthrough:
362     // In both of these cases, putting the 'other' feature on the end of the vector will
363     // result in the same effect as placing it immediately after the current feature.
364     if (ItRNoFullFP16 < ItRFP16FML)
365       Features.push_back("-fp16fml");
366     else if (ItRNoFullFP16 > ItRFP16FML)
367       Features.push_back("+fullfp16");
368   }
369 
370   // FIXME: this needs reimplementation too after the TargetParser rewrite
371   //
372   // Context sensitive meaning of Crypto:
373   // 1) For Arch >= ARMv8.4a:  crypto = sm4 + sha3 + sha2 + aes
374   // 2) For Arch <= ARMv8.3a:  crypto = sha2 + aes
375   const auto ItBegin = Features.begin();
376   const auto ItEnd = Features.end();
377   const auto ItRBegin = Features.rbegin();
378   const auto ItREnd = Features.rend();
379   const auto ItRCrypto = std::find(ItRBegin, ItREnd, "+crypto");
380   const auto ItRNoCrypto = std::find(ItRBegin, ItREnd, "-crypto");
381   const auto HasCrypto  = ItRCrypto != ItREnd;
382   const auto HasNoCrypto = ItRNoCrypto != ItREnd;
383   const ptrdiff_t PosCrypto = ItRCrypto - ItRBegin;
384   const ptrdiff_t PosNoCrypto = ItRNoCrypto - ItRBegin;
385 
386   bool NoCrypto = false;
387   if (HasCrypto && HasNoCrypto) {
388     if (PosNoCrypto < PosCrypto)
389       NoCrypto = true;
390   }
391 
392   if (std::find(ItBegin, ItEnd, "+v8.4a") != ItEnd ||
393       std::find(ItBegin, ItEnd, "+v9a") != ItEnd ||
394       std::find(ItBegin, ItEnd, "+v9.1a") != ItEnd ||
395       std::find(ItBegin, ItEnd, "+v9.2a") != ItEnd) {
396     if (HasCrypto && !NoCrypto) {
397       // Check if we have NOT disabled an algorithm with something like:
398       //   +crypto, -algorithm
399       // And if "-algorithm" does not occur, we enable that crypto algorithm.
400       const bool HasSM4  = (std::find(ItBegin, ItEnd, "-sm4") == ItEnd);
401       const bool HasSHA3 = (std::find(ItBegin, ItEnd, "-sha3") == ItEnd);
402       const bool HasSHA2 = (std::find(ItBegin, ItEnd, "-sha2") == ItEnd);
403       const bool HasAES  = (std::find(ItBegin, ItEnd, "-aes") == ItEnd);
404       if (HasSM4)
405         Features.push_back("+sm4");
406       if (HasSHA3)
407         Features.push_back("+sha3");
408       if (HasSHA2)
409         Features.push_back("+sha2");
410       if (HasAES)
411         Features.push_back("+aes");
412     } else if (HasNoCrypto) {
413       // Check if we have NOT enabled a crypto algorithm with something like:
414       //   -crypto, +algorithm
415       // And if "+algorithm" does not occur, we disable that crypto algorithm.
416       const bool HasSM4  = (std::find(ItBegin, ItEnd, "+sm4") != ItEnd);
417       const bool HasSHA3 = (std::find(ItBegin, ItEnd, "+sha3") != ItEnd);
418       const bool HasSHA2 = (std::find(ItBegin, ItEnd, "+sha2") != ItEnd);
419       const bool HasAES  = (std::find(ItBegin, ItEnd, "+aes") != ItEnd);
420       if (!HasSM4)
421         Features.push_back("-sm4");
422       if (!HasSHA3)
423         Features.push_back("-sha3");
424       if (!HasSHA2)
425         Features.push_back("-sha2");
426       if (!HasAES)
427         Features.push_back("-aes");
428     }
429   } else {
430     if (HasCrypto && !NoCrypto) {
431       const bool HasSHA2 = (std::find(ItBegin, ItEnd, "-sha2") == ItEnd);
432       const bool HasAES = (std::find(ItBegin, ItEnd, "-aes") == ItEnd);
433       if (HasSHA2)
434         Features.push_back("+sha2");
435       if (HasAES)
436         Features.push_back("+aes");
437     } else if (HasNoCrypto) {
438       const bool HasSHA2 = (std::find(ItBegin, ItEnd, "+sha2") != ItEnd);
439       const bool HasAES  = (std::find(ItBegin, ItEnd, "+aes") != ItEnd);
440       const bool HasV82a = (std::find(ItBegin, ItEnd, "+v8.2a") != ItEnd);
441       const bool HasV83a = (std::find(ItBegin, ItEnd, "+v8.3a") != ItEnd);
442       const bool HasV84a = (std::find(ItBegin, ItEnd, "+v8.4a") != ItEnd);
443       if (!HasSHA2)
444         Features.push_back("-sha2");
445       if (!HasAES)
446         Features.push_back("-aes");
447       if (HasV82a || HasV83a || HasV84a) {
448         Features.push_back("-sm4");
449         Features.push_back("-sha3");
450       }
451     }
452   }
453 
454   const char *Archs[] = {"+v8.6a", "+v8.7a", "+v9.1a", "+v9.2a"};
455   auto Pos = std::find_first_of(Features.begin(), Features.end(),
456                                 std::begin(Archs), std::end(Archs));
457   if (Pos != std::end(Features))
458     Pos = Features.insert(std::next(Pos), {"+i8mm", "+bf16"});
459 
460   if (Arg *A = Args.getLastArg(options::OPT_mno_unaligned_access,
461                                options::OPT_munaligned_access)) {
462     if (A->getOption().matches(options::OPT_mno_unaligned_access))
463       Features.push_back("+strict-align");
464   } else if (Triple.isOSOpenBSD())
465     Features.push_back("+strict-align");
466 
467   if (Args.hasArg(options::OPT_ffixed_x1))
468     Features.push_back("+reserve-x1");
469 
470   if (Args.hasArg(options::OPT_ffixed_x2))
471     Features.push_back("+reserve-x2");
472 
473   if (Args.hasArg(options::OPT_ffixed_x3))
474     Features.push_back("+reserve-x3");
475 
476   if (Args.hasArg(options::OPT_ffixed_x4))
477     Features.push_back("+reserve-x4");
478 
479   if (Args.hasArg(options::OPT_ffixed_x5))
480     Features.push_back("+reserve-x5");
481 
482   if (Args.hasArg(options::OPT_ffixed_x6))
483     Features.push_back("+reserve-x6");
484 
485   if (Args.hasArg(options::OPT_ffixed_x7))
486     Features.push_back("+reserve-x7");
487 
488   if (Args.hasArg(options::OPT_ffixed_x9))
489     Features.push_back("+reserve-x9");
490 
491   if (Args.hasArg(options::OPT_ffixed_x10))
492     Features.push_back("+reserve-x10");
493 
494   if (Args.hasArg(options::OPT_ffixed_x11))
495     Features.push_back("+reserve-x11");
496 
497   if (Args.hasArg(options::OPT_ffixed_x12))
498     Features.push_back("+reserve-x12");
499 
500   if (Args.hasArg(options::OPT_ffixed_x13))
501     Features.push_back("+reserve-x13");
502 
503   if (Args.hasArg(options::OPT_ffixed_x14))
504     Features.push_back("+reserve-x14");
505 
506   if (Args.hasArg(options::OPT_ffixed_x15))
507     Features.push_back("+reserve-x15");
508 
509   if (Args.hasArg(options::OPT_ffixed_x18))
510     Features.push_back("+reserve-x18");
511 
512   if (Args.hasArg(options::OPT_ffixed_x20))
513     Features.push_back("+reserve-x20");
514 
515   if (Args.hasArg(options::OPT_ffixed_x21))
516     Features.push_back("+reserve-x21");
517 
518   if (Args.hasArg(options::OPT_ffixed_x22))
519     Features.push_back("+reserve-x22");
520 
521   if (Args.hasArg(options::OPT_ffixed_x23))
522     Features.push_back("+reserve-x23");
523 
524   if (Args.hasArg(options::OPT_ffixed_x24))
525     Features.push_back("+reserve-x24");
526 
527   if (Args.hasArg(options::OPT_ffixed_x25))
528     Features.push_back("+reserve-x25");
529 
530   if (Args.hasArg(options::OPT_ffixed_x26))
531     Features.push_back("+reserve-x26");
532 
533   if (Args.hasArg(options::OPT_ffixed_x27))
534     Features.push_back("+reserve-x27");
535 
536   if (Args.hasArg(options::OPT_ffixed_x28))
537     Features.push_back("+reserve-x28");
538 
539   if (Args.hasArg(options::OPT_ffixed_x30))
540     Features.push_back("+reserve-x30");
541 
542   if (Args.hasArg(options::OPT_fcall_saved_x8))
543     Features.push_back("+call-saved-x8");
544 
545   if (Args.hasArg(options::OPT_fcall_saved_x9))
546     Features.push_back("+call-saved-x9");
547 
548   if (Args.hasArg(options::OPT_fcall_saved_x10))
549     Features.push_back("+call-saved-x10");
550 
551   if (Args.hasArg(options::OPT_fcall_saved_x11))
552     Features.push_back("+call-saved-x11");
553 
554   if (Args.hasArg(options::OPT_fcall_saved_x12))
555     Features.push_back("+call-saved-x12");
556 
557   if (Args.hasArg(options::OPT_fcall_saved_x13))
558     Features.push_back("+call-saved-x13");
559 
560   if (Args.hasArg(options::OPT_fcall_saved_x14))
561     Features.push_back("+call-saved-x14");
562 
563   if (Args.hasArg(options::OPT_fcall_saved_x15))
564     Features.push_back("+call-saved-x15");
565 
566   if (Args.hasArg(options::OPT_fcall_saved_x18))
567     Features.push_back("+call-saved-x18");
568 
569   if (Args.hasArg(options::OPT_mno_neg_immediates))
570     Features.push_back("+no-neg-immediates");
571 }
572