1 //===--- RISCV.cpp - RISCV 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 "RISCV.h"
10 #include "clang/Basic/CharInfo.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/ADT/Optional.h"
16 #include "llvm/Support/TargetParser.h"
17 #include "llvm/Support/raw_ostream.h"
18 #include "ToolChains/CommonArgs.h"
19 
20 using namespace clang::driver;
21 using namespace clang::driver::tools;
22 using namespace clang;
23 using namespace llvm::opt;
24 
25 namespace {
26 // Represents the major and version number components of a RISC-V extension
27 struct RISCVExtensionVersion {
28   StringRef Major;
29   StringRef Minor;
30 };
31 } // end anonymous namespace
32 
33 static StringRef getExtensionTypeDesc(StringRef Ext) {
34   if (Ext.startswith("sx"))
35     return "non-standard supervisor-level extension";
36   if (Ext.startswith("s"))
37     return "standard supervisor-level extension";
38   if (Ext.startswith("x"))
39     return "non-standard user-level extension";
40   if (Ext.startswith("z"))
41     return "standard user-level extension";
42   return StringRef();
43 }
44 
45 static StringRef getExtensionType(StringRef Ext) {
46   if (Ext.startswith("sx"))
47     return "sx";
48   if (Ext.startswith("s"))
49     return "s";
50   if (Ext.startswith("x"))
51     return "x";
52   if (Ext.startswith("z"))
53     return "z";
54   return StringRef();
55 }
56 
57 // If the extension is supported as experimental, return the version of that
58 // extension that the compiler currently supports.
59 static Optional<RISCVExtensionVersion>
60 isExperimentalExtension(StringRef Ext) {
61   if (Ext == "b" || Ext == "zbb" || Ext == "zbc" || Ext == "zbe" ||
62       Ext == "zbf" || Ext == "zbm" || Ext == "zbp" || Ext == "zbr" ||
63       Ext == "zbs" || Ext == "zbt" || Ext == "zbproposedc")
64     return RISCVExtensionVersion{"0", "92"};
65   return None;
66 }
67 
68 static bool isSupportedExtension(StringRef Ext) {
69   // LLVM supports "z" extensions which are marked as experimental.
70   if (isExperimentalExtension(Ext))
71     return true;
72 
73   // LLVM does not support "sx", "s" nor "x" extensions.
74   return false;
75 }
76 
77 // Extensions may have a version number, and may be separated by
78 // an underscore '_' e.g.: rv32i2_m2.
79 // Version number is divided into major and minor version numbers,
80 // separated by a 'p'. If the minor version is 0 then 'p0' can be
81 // omitted from the version string. E.g., rv32i2p0, rv32i2, rv32i2p1.
82 static bool getExtensionVersion(const Driver &D, const ArgList &Args,
83                                 StringRef MArch, StringRef Ext, StringRef In,
84                                 std::string &Major, std::string &Minor) {
85   Major = std::string(In.take_while(isDigit));
86   In = In.substr(Major.size());
87 
88   if (Major.size() && In.consume_front("p")) {
89     Minor = std::string(In.take_while(isDigit));
90     In = In.substr(Major.size());
91 
92     // Expected 'p' to be followed by minor version number.
93     if (Minor.empty()) {
94       std::string Error =
95         "minor version number missing after 'p' for extension";
96       D.Diag(diag::err_drv_invalid_riscv_ext_arch_name)
97         << MArch << Error << Ext;
98       return false;
99     }
100   }
101 
102   // If experimental extension, require use of current version number number
103   if (auto ExperimentalExtension = isExperimentalExtension(Ext)) {
104     if (!Args.hasArg(options::OPT_menable_experimental_extensions)) {
105       std::string Error =
106           "requires '-menable-experimental-extensions' for experimental extension";
107       D.Diag(diag::err_drv_invalid_riscv_ext_arch_name)
108           << MArch << Error << Ext;
109       return false;
110     } else if (Major.empty() && Minor.empty()) {
111       std::string Error =
112           "experimental extension requires explicit version number";
113       D.Diag(diag::err_drv_invalid_riscv_ext_arch_name)
114           << MArch << Error << Ext;
115       return false;
116     }
117     auto SupportedVers = *ExperimentalExtension;
118     if (Major != SupportedVers.Major || Minor != SupportedVers.Minor) {
119       std::string Error =
120           "unsupported version number " + Major;
121       if (!Minor.empty())
122         Error += "." + Minor;
123       Error += " for experimental extension (this compiler supports "
124             + SupportedVers.Major.str() + "."
125             + SupportedVers.Minor.str() + ")";
126 
127       D.Diag(diag::err_drv_invalid_riscv_ext_arch_name)
128           << MArch << Error << Ext;
129       return false;
130     }
131     return true;
132   }
133 
134   // Allow extensions to declare no version number
135   if (Major.empty() && Minor.empty())
136     return true;
137 
138   // TODO: Handle supported extensions with version number.
139   std::string Error = "unsupported version number " + Major;
140   if (!Minor.empty())
141     Error += "." + Minor;
142   Error += " for extension";
143   D.Diag(diag::err_drv_invalid_riscv_ext_arch_name) << MArch << Error << Ext;
144 
145   return false;
146 }
147 
148 // Handle other types of extensions other than the standard
149 // general purpose and standard user-level extensions.
150 // Parse the ISA string containing non-standard user-level
151 // extensions, standard supervisor-level extensions and
152 // non-standard supervisor-level extensions.
153 // These extensions start with 'z', 'x', 's', 'sx' prefixes, follow a
154 // canonical order, might have a version number (major, minor)
155 // and are separated by a single underscore '_'.
156 // Set the hardware features for the extensions that are supported.
157 static void getExtensionFeatures(const Driver &D,
158                                  const ArgList &Args,
159                                  std::vector<StringRef> &Features,
160                                  StringRef &MArch, StringRef &Exts) {
161   if (Exts.empty())
162     return;
163 
164   // Multi-letter extensions are seperated by a single underscore
165   // as described in RISC-V User-Level ISA V2.2.
166   SmallVector<StringRef, 8> Split;
167   Exts.split(Split, StringRef("_"));
168 
169   SmallVector<StringRef, 4> Prefix{"z", "x", "s", "sx"};
170   auto I = Prefix.begin();
171   auto E = Prefix.end();
172 
173   SmallVector<StringRef, 8> AllExts;
174 
175   for (StringRef Ext : Split) {
176     if (Ext.empty()) {
177       D.Diag(diag::err_drv_invalid_riscv_arch_name) << MArch
178         << "extension name missing after separator '_'";
179       return;
180     }
181 
182     StringRef Type = getExtensionType(Ext);
183     StringRef Desc = getExtensionTypeDesc(Ext);
184     auto Pos = Ext.find_if(isDigit);
185     StringRef Name(Ext.substr(0, Pos));
186     StringRef Vers(Ext.substr(Pos));
187 
188     if (Type.empty()) {
189       D.Diag(diag::err_drv_invalid_riscv_ext_arch_name)
190         << MArch << "invalid extension prefix" << Ext;
191       return;
192     }
193 
194     // Check ISA extensions are specified in the canonical order.
195     while (I != E && *I != Type)
196       ++I;
197 
198     if (I == E) {
199       std::string Error = std::string(Desc);
200       Error += " not given in canonical order";
201       D.Diag(diag::err_drv_invalid_riscv_ext_arch_name)
202         << MArch <<  Error << Ext;
203       return;
204     }
205 
206     // The order is OK, do not advance I to the next prefix
207     // to allow repeated extension type, e.g.: rv32ixabc_xdef.
208 
209     if (Name.size() == Type.size()) {
210       std::string Error = std::string(Desc);
211       Error += " name missing after";
212       D.Diag(diag::err_drv_invalid_riscv_ext_arch_name)
213         << MArch << Error << Type;
214       return;
215     }
216 
217     std::string Major, Minor;
218     if (!getExtensionVersion(D, Args, MArch, Name, Vers, Major, Minor))
219       return;
220 
221     // Check if duplicated extension.
222     if (llvm::is_contained(AllExts, Name)) {
223       std::string Error = "duplicated ";
224       Error += Desc;
225       D.Diag(diag::err_drv_invalid_riscv_ext_arch_name)
226         << MArch << Error << Name;
227       return;
228     }
229 
230     // Extension format is correct, keep parsing the extensions.
231     // TODO: Save Type, Name, Major, Minor to avoid parsing them later.
232     AllExts.push_back(Name);
233   }
234 
235   // Set target features.
236   // TODO: Hardware features to be handled in Support/TargetParser.cpp.
237   // TODO: Use version number when setting target features.
238   for (auto Ext : AllExts) {
239     if (!isSupportedExtension(Ext)) {
240       StringRef Desc = getExtensionTypeDesc(getExtensionType(Ext));
241       std::string Error = "unsupported ";
242       Error += Desc;
243       D.Diag(diag::err_drv_invalid_riscv_ext_arch_name)
244         << MArch << Error << Ext;
245       return;
246     }
247     if (isExperimentalExtension(Ext))
248       Features.push_back(Args.MakeArgString("+experimental-" + Ext));
249     else
250       Features.push_back(Args.MakeArgString("+" + Ext));
251   }
252 }
253 
254 // Returns false if an error is diagnosed.
255 static bool getArchFeatures(const Driver &D, StringRef MArch,
256                             std::vector<StringRef> &Features,
257                             const ArgList &Args) {
258   // RISC-V ISA strings must be lowercase.
259   if (llvm::any_of(MArch, [](char c) { return isupper(c); })) {
260     D.Diag(diag::err_drv_invalid_riscv_arch_name)
261         << MArch << "string must be lowercase";
262     return false;
263   }
264 
265   // ISA string must begin with rv32 or rv64.
266   if (!(MArch.startswith("rv32") || MArch.startswith("rv64")) ||
267       (MArch.size() < 5)) {
268     D.Diag(diag::err_drv_invalid_riscv_arch_name)
269         << MArch << "string must begin with rv32{i,e,g} or rv64{i,g}";
270     return false;
271   }
272 
273   bool HasRV64 = MArch.startswith("rv64");
274 
275   // The canonical order specified in ISA manual.
276   // Ref: Table 22.1 in RISC-V User-Level ISA V2.2
277   StringRef StdExts = "mafdqlcbjtpvn";
278   bool HasF = false, HasD = false;
279   char Baseline = MArch[4];
280 
281   // First letter should be 'e', 'i' or 'g'.
282   switch (Baseline) {
283   default:
284     D.Diag(diag::err_drv_invalid_riscv_arch_name)
285         << MArch << "first letter should be 'e', 'i' or 'g'";
286     return false;
287   case 'e': {
288     StringRef Error;
289     // Currently LLVM does not support 'e'.
290     // Extension 'e' is not allowed in rv64.
291     if (HasRV64)
292       Error = "standard user-level extension 'e' requires 'rv32'";
293     else
294       Error = "unsupported standard user-level extension 'e'";
295     D.Diag(diag::err_drv_invalid_riscv_arch_name) << MArch << Error;
296     return false;
297   }
298   case 'i':
299     break;
300   case 'g':
301     // g = imafd
302     StdExts = StdExts.drop_front(4);
303     Features.push_back("+m");
304     Features.push_back("+a");
305     Features.push_back("+f");
306     Features.push_back("+d");
307     HasF = true;
308     HasD = true;
309     break;
310   }
311 
312   // Skip rvxxx
313   StringRef Exts = MArch.substr(5);
314 
315   // Remove multi-letter standard extensions, non-standard extensions and
316   // supervisor-level extensions. They have 'z', 'x', 's', 'sx' prefixes.
317   // Parse them at the end.
318   // Find the very first occurrence of 's', 'x' or 'z'.
319   StringRef OtherExts;
320   size_t Pos = Exts.find_first_of("zsx");
321   if (Pos != StringRef::npos) {
322     OtherExts = Exts.substr(Pos);
323     Exts = Exts.substr(0, Pos);
324   }
325 
326   std::string Major, Minor;
327   if (!getExtensionVersion(D, Args, MArch, std::string(1, Baseline), Exts,
328                            Major, Minor))
329     return false;
330 
331   // Consume the base ISA version number and any '_' between rvxxx and the
332   // first extension
333   Exts = Exts.drop_front(Major.size());
334   if (!Minor.empty())
335     Exts = Exts.drop_front(Minor.size() + 1 /*'p'*/);
336   Exts.consume_front("_");
337 
338   // TODO: Use version number when setting target features
339 
340   auto StdExtsItr = StdExts.begin();
341   auto StdExtsEnd = StdExts.end();
342 
343   for (auto I = Exts.begin(), E = Exts.end(); I != E; ) {
344     char c = *I;
345 
346     // Check ISA extensions are specified in the canonical order.
347     while (StdExtsItr != StdExtsEnd && *StdExtsItr != c)
348       ++StdExtsItr;
349 
350     if (StdExtsItr == StdExtsEnd) {
351       // Either c contains a valid extension but it was not given in
352       // canonical order or it is an invalid extension.
353       StringRef Error;
354       if (StdExts.contains(c))
355         Error = "standard user-level extension not given in canonical order";
356       else
357         Error = "invalid standard user-level extension";
358       D.Diag(diag::err_drv_invalid_riscv_ext_arch_name)
359           << MArch << Error << std::string(1, c);
360       return false;
361     }
362 
363     // Move to next char to prevent repeated letter.
364     ++StdExtsItr;
365 
366     std::string Next, Major, Minor;
367     if (std::next(I) != E)
368       Next = std::string(std::next(I), E);
369     if (!getExtensionVersion(D, Args, MArch, std::string(1, c), Next, Major,
370                              Minor))
371       return false;
372 
373     // The order is OK, then push it into features.
374     // TODO: Use version number when setting target features
375     switch (c) {
376     default:
377       // Currently LLVM supports only "mafdc".
378       D.Diag(diag::err_drv_invalid_riscv_ext_arch_name)
379           << MArch << "unsupported standard user-level extension"
380           << std::string(1, c);
381       return false;
382     case 'm':
383       Features.push_back("+m");
384       break;
385     case 'a':
386       Features.push_back("+a");
387       break;
388     case 'f':
389       Features.push_back("+f");
390       HasF = true;
391       break;
392     case 'd':
393       Features.push_back("+d");
394       HasD = true;
395       break;
396     case 'c':
397       Features.push_back("+c");
398       break;
399     case 'b':
400       Features.push_back("+experimental-b");
401       break;
402     }
403 
404     // Consume full extension name and version, including any optional '_'
405     // between this extension and the next
406     ++I;
407     I += Major.size();
408     if (Minor.size())
409       I += Minor.size() + 1 /*'p'*/;
410     if (*I == '_')
411       ++I;
412   }
413 
414   // Dependency check.
415   // It's illegal to specify the 'd' (double-precision floating point)
416   // extension without also specifying the 'f' (single precision
417   // floating-point) extension.
418   if (HasD && !HasF) {
419     D.Diag(diag::err_drv_invalid_riscv_arch_name)
420         << MArch << "d requires f extension to also be specified";
421     return false;
422   }
423 
424   // Additional dependency checks.
425   // TODO: The 'q' extension requires rv64.
426   // TODO: It is illegal to specify 'e' extensions with 'f' and 'd'.
427 
428   // Handle all other types of extensions.
429   getExtensionFeatures(D, Args, Features, MArch, OtherExts);
430 
431   return true;
432 }
433 
434 void riscv::getRISCVTargetFeatures(const Driver &D, const llvm::Triple &Triple,
435                                    const ArgList &Args,
436                                    std::vector<StringRef> &Features) {
437   StringRef MArch = getRISCVArch(Args, Triple);
438 
439   if (!getArchFeatures(D, MArch, Features, Args))
440     return;
441 
442   // Handle features corresponding to "-ffixed-X" options
443   if (Args.hasArg(options::OPT_ffixed_x1))
444     Features.push_back("+reserve-x1");
445   if (Args.hasArg(options::OPT_ffixed_x2))
446     Features.push_back("+reserve-x2");
447   if (Args.hasArg(options::OPT_ffixed_x3))
448     Features.push_back("+reserve-x3");
449   if (Args.hasArg(options::OPT_ffixed_x4))
450     Features.push_back("+reserve-x4");
451   if (Args.hasArg(options::OPT_ffixed_x5))
452     Features.push_back("+reserve-x5");
453   if (Args.hasArg(options::OPT_ffixed_x6))
454     Features.push_back("+reserve-x6");
455   if (Args.hasArg(options::OPT_ffixed_x7))
456     Features.push_back("+reserve-x7");
457   if (Args.hasArg(options::OPT_ffixed_x8))
458     Features.push_back("+reserve-x8");
459   if (Args.hasArg(options::OPT_ffixed_x9))
460     Features.push_back("+reserve-x9");
461   if (Args.hasArg(options::OPT_ffixed_x10))
462     Features.push_back("+reserve-x10");
463   if (Args.hasArg(options::OPT_ffixed_x11))
464     Features.push_back("+reserve-x11");
465   if (Args.hasArg(options::OPT_ffixed_x12))
466     Features.push_back("+reserve-x12");
467   if (Args.hasArg(options::OPT_ffixed_x13))
468     Features.push_back("+reserve-x13");
469   if (Args.hasArg(options::OPT_ffixed_x14))
470     Features.push_back("+reserve-x14");
471   if (Args.hasArg(options::OPT_ffixed_x15))
472     Features.push_back("+reserve-x15");
473   if (Args.hasArg(options::OPT_ffixed_x16))
474     Features.push_back("+reserve-x16");
475   if (Args.hasArg(options::OPT_ffixed_x17))
476     Features.push_back("+reserve-x17");
477   if (Args.hasArg(options::OPT_ffixed_x18))
478     Features.push_back("+reserve-x18");
479   if (Args.hasArg(options::OPT_ffixed_x19))
480     Features.push_back("+reserve-x19");
481   if (Args.hasArg(options::OPT_ffixed_x20))
482     Features.push_back("+reserve-x20");
483   if (Args.hasArg(options::OPT_ffixed_x21))
484     Features.push_back("+reserve-x21");
485   if (Args.hasArg(options::OPT_ffixed_x22))
486     Features.push_back("+reserve-x22");
487   if (Args.hasArg(options::OPT_ffixed_x23))
488     Features.push_back("+reserve-x23");
489   if (Args.hasArg(options::OPT_ffixed_x24))
490     Features.push_back("+reserve-x24");
491   if (Args.hasArg(options::OPT_ffixed_x25))
492     Features.push_back("+reserve-x25");
493   if (Args.hasArg(options::OPT_ffixed_x26))
494     Features.push_back("+reserve-x26");
495   if (Args.hasArg(options::OPT_ffixed_x27))
496     Features.push_back("+reserve-x27");
497   if (Args.hasArg(options::OPT_ffixed_x28))
498     Features.push_back("+reserve-x28");
499   if (Args.hasArg(options::OPT_ffixed_x29))
500     Features.push_back("+reserve-x29");
501   if (Args.hasArg(options::OPT_ffixed_x30))
502     Features.push_back("+reserve-x30");
503   if (Args.hasArg(options::OPT_ffixed_x31))
504     Features.push_back("+reserve-x31");
505 
506   // -mrelax is default, unless -mno-relax is specified.
507   if (Args.hasFlag(options::OPT_mrelax, options::OPT_mno_relax, true))
508     Features.push_back("+relax");
509   else
510     Features.push_back("-relax");
511 
512   // GCC Compatibility: -mno-save-restore is default, unless -msave-restore is
513   // specified.
514   if (Args.hasFlag(options::OPT_msave_restore, options::OPT_mno_save_restore, false))
515     Features.push_back("+save-restore");
516   else
517     Features.push_back("-save-restore");
518 
519   // Now add any that the user explicitly requested on the command line,
520   // which may override the defaults.
521   handleTargetFeaturesGroup(Args, Features, options::OPT_m_riscv_Features_Group);
522 }
523 
524 StringRef riscv::getRISCVABI(const ArgList &Args, const llvm::Triple &Triple) {
525   assert((Triple.getArch() == llvm::Triple::riscv32 ||
526           Triple.getArch() == llvm::Triple::riscv64) &&
527          "Unexpected triple");
528 
529   // GCC's logic around choosing a default `-mabi=` is complex. If GCC is not
530   // configured using `--with-abi=`, then the logic for the default choice is
531   // defined in config.gcc. This function is based on the logic in GCC 9.2.0. We
532   // deviate from GCC's default only on baremetal targets (UnknownOS) where
533   // neither `-march` nor `-mabi` is specified.
534   //
535   // The logic uses the following, in order:
536   // 1. Explicit choices using `--with-abi=`
537   // 2. A default based on `--with-arch=`, if provided
538   // 3. A default based on the target triple's arch
539   //
540   // The logic in config.gcc is a little circular but it is not inconsistent.
541   //
542   // Clang does not have `--with-arch=` or `--with-abi=`, so we use `-march=`
543   // and `-mabi=` respectively instead.
544 
545   // 1. If `-mabi=` is specified, use it.
546   if (const Arg *A = Args.getLastArg(options::OPT_mabi_EQ))
547     return A->getValue();
548 
549   // 2. Choose a default based on `-march=`
550   //
551   // rv32g | rv32*d -> ilp32d
552   // rv32e -> ilp32e
553   // rv32* -> ilp32
554   // rv64g | rv64*d -> lp64d
555   // rv64* -> lp64
556   if (const Arg *A = Args.getLastArg(options::OPT_march_EQ)) {
557     StringRef MArch = A->getValue();
558 
559     if (MArch.startswith_lower("rv32")) {
560       // FIXME: parse `March` to find `D` extension properly
561       if (MArch.substr(4).contains_lower("d") ||
562           MArch.startswith_lower("rv32g"))
563         return "ilp32d";
564       else if (MArch.startswith_lower("rv32e"))
565         return "ilp32e";
566       else
567         return "ilp32";
568     } else if (MArch.startswith_lower("rv64")) {
569       // FIXME: parse `March` to find `D` extension properly
570       if (MArch.substr(4).contains_lower("d") ||
571           MArch.startswith_lower("rv64g"))
572         return "lp64d";
573       else
574         return "lp64";
575     }
576   }
577 
578   // 3. Choose a default based on the triple
579   //
580   // We deviate from GCC's defaults here:
581   // - On `riscv{XLEN}-unknown-elf` we use the integer calling convention only.
582   // - On all other OSs we use the double floating point calling convention.
583   if (Triple.getArch() == llvm::Triple::riscv32) {
584     if (Triple.getOS() == llvm::Triple::UnknownOS)
585       return "ilp32";
586     else
587       return "ilp32d";
588   } else {
589     if (Triple.getOS() == llvm::Triple::UnknownOS)
590       return "lp64";
591     else
592       return "lp64d";
593   }
594 }
595 
596 StringRef riscv::getRISCVArch(const llvm::opt::ArgList &Args,
597                               const llvm::Triple &Triple) {
598   assert((Triple.getArch() == llvm::Triple::riscv32 ||
599           Triple.getArch() == llvm::Triple::riscv64) &&
600          "Unexpected triple");
601 
602   // GCC's logic around choosing a default `-march=` is complex. If GCC is not
603   // configured using `--with-arch=`, then the logic for the default choice is
604   // defined in config.gcc. This function is based on the logic in GCC 9.2.0. We
605   // deviate from GCC's default only on baremetal targets (UnknownOS) where
606   // neither `-march` nor `-mabi` is specified.
607   //
608   // The logic uses the following, in order:
609   // 1. Explicit choices using `--with-arch=`
610   // 2. A default based on `--with-abi=`, if provided
611   // 3. A default based on the target triple's arch
612   //
613   // The logic in config.gcc is a little circular but it is not inconsistent.
614   //
615   // Clang does not have `--with-arch=` or `--with-abi=`, so we use `-march=`
616   // and `-mabi=` respectively instead.
617   //
618   // Clang does not yet support MULTILIB_REUSE, so we use `rv{XLEN}imafdc`
619   // instead of `rv{XLEN}gc` though they are (currently) equivalent.
620 
621   // 1. If `-march=` is specified, use it.
622   if (const Arg *A = Args.getLastArg(options::OPT_march_EQ))
623     return A->getValue();
624 
625   // 2. Choose a default based on `-mabi=`
626   //
627   // ilp32e -> rv32e
628   // ilp32 | ilp32f | ilp32d -> rv32imafdc
629   // lp64 | lp64f | lp64d -> rv64imafdc
630   if (const Arg *A = Args.getLastArg(options::OPT_mabi_EQ)) {
631     StringRef MABI = A->getValue();
632 
633     if (MABI.equals_lower("ilp32e"))
634       return "rv32e";
635     else if (MABI.startswith_lower("ilp32"))
636       return "rv32imafdc";
637     else if (MABI.startswith_lower("lp64"))
638       return "rv64imafdc";
639   }
640 
641   // 3. Choose a default based on the triple
642   //
643   // We deviate from GCC's defaults here:
644   // - On `riscv{XLEN}-unknown-elf` we default to `rv{XLEN}imac`
645   // - On all other OSs we use `rv{XLEN}imafdc` (equivalent to `rv{XLEN}gc`)
646   if (Triple.getArch() == llvm::Triple::riscv32) {
647     if (Triple.getOS() == llvm::Triple::UnknownOS)
648       return "rv32imac";
649     else
650       return "rv32imafdc";
651   } else {
652     if (Triple.getOS() == llvm::Triple::UnknownOS)
653       return "rv64imac";
654     else
655       return "rv64imafdc";
656   }
657 }
658