xref: /freebsd/contrib/llvm-project/clang/lib/Driver/ToolChains/Arch/LoongArch.cpp (revision 700637cbb5e582861067a11aaca4d053546871d2)
1 //===--- LoongArch.cpp - LoongArch 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 "LoongArch.h"
10 #include "../Clang.h"
11 #include "clang/Basic/DiagnosticDriver.h"
12 #include "clang/Driver/CommonArgs.h"
13 #include "clang/Driver/Driver.h"
14 #include "clang/Driver/Options.h"
15 #include "llvm/TargetParser/Host.h"
16 #include "llvm/TargetParser/LoongArchTargetParser.h"
17 
18 using namespace clang::driver;
19 using namespace clang::driver::tools;
20 using namespace clang;
21 using namespace llvm::opt;
22 
getLoongArchABI(const Driver & D,const ArgList & Args,const llvm::Triple & Triple)23 StringRef loongarch::getLoongArchABI(const Driver &D, const ArgList &Args,
24                                      const llvm::Triple &Triple) {
25   assert((Triple.getArch() == llvm::Triple::loongarch32 ||
26           Triple.getArch() == llvm::Triple::loongarch64) &&
27          "Unexpected triple");
28   bool IsLA32 = Triple.getArch() == llvm::Triple::loongarch32;
29 
30   // Record -mabi value for later use.
31   const Arg *MABIArg = Args.getLastArg(options::OPT_mabi_EQ);
32   StringRef MABIValue;
33   if (MABIArg) {
34     MABIValue = MABIArg->getValue();
35   }
36 
37   // Parse -mfpu value for later use.
38   const Arg *MFPUArg = Args.getLastArg(options::OPT_mfpu_EQ);
39   int FPU = -1;
40   if (MFPUArg) {
41     StringRef V = MFPUArg->getValue();
42     if (V == "64")
43       FPU = 64;
44     else if (V == "32")
45       FPU = 32;
46     else if (V == "0" || V == "none")
47       FPU = 0;
48     else
49       D.Diag(diag::err_drv_loongarch_invalid_mfpu_EQ) << V;
50   }
51 
52   // Check -m*-float firstly since they have highest priority.
53   if (const Arg *A = Args.getLastArg(options::OPT_mdouble_float,
54                                      options::OPT_msingle_float,
55                                      options::OPT_msoft_float)) {
56     StringRef ImpliedABI;
57     int ImpliedFPU = -1;
58     if (A->getOption().matches(options::OPT_mdouble_float)) {
59       ImpliedABI = IsLA32 ? "ilp32d" : "lp64d";
60       ImpliedFPU = 64;
61     }
62     if (A->getOption().matches(options::OPT_msingle_float)) {
63       ImpliedABI = IsLA32 ? "ilp32f" : "lp64f";
64       ImpliedFPU = 32;
65     }
66     if (A->getOption().matches(options::OPT_msoft_float)) {
67       ImpliedABI = IsLA32 ? "ilp32s" : "lp64s";
68       ImpliedFPU = 0;
69     }
70 
71     // Check `-mabi=` and `-mfpu=` settings and report if they conflict with
72     // the higher-priority settings implied by -m*-float.
73     //
74     // ImpliedABI and ImpliedFPU are guaranteed to have valid values because
75     // one of the match arms must match if execution can arrive here at all.
76     if (!MABIValue.empty() && ImpliedABI != MABIValue)
77       D.Diag(diag::warn_drv_loongarch_conflicting_implied_val)
78           << MABIArg->getAsString(Args) << A->getAsString(Args) << ImpliedABI;
79 
80     if (FPU != -1 && ImpliedFPU != FPU)
81       D.Diag(diag::warn_drv_loongarch_conflicting_implied_val)
82           << MFPUArg->getAsString(Args) << A->getAsString(Args) << ImpliedFPU;
83 
84     return ImpliedABI;
85   }
86 
87   // If `-mabi=` is specified, use it.
88   if (!MABIValue.empty())
89     return MABIValue;
90 
91   // Select abi based on -mfpu=xx.
92   switch (FPU) {
93   case 64:
94     return IsLA32 ? "ilp32d" : "lp64d";
95   case 32:
96     return IsLA32 ? "ilp32f" : "lp64f";
97   case 0:
98     return IsLA32 ? "ilp32s" : "lp64s";
99   }
100 
101   // Choose a default based on the triple.
102   // Honor the explicit ABI modifier suffix in triple's environment part if
103   // present, falling back to {ILP32,LP64}D otherwise.
104   switch (Triple.getEnvironment()) {
105   case llvm::Triple::GNUSF:
106   case llvm::Triple::MuslSF:
107     return IsLA32 ? "ilp32s" : "lp64s";
108   case llvm::Triple::GNUF32:
109   case llvm::Triple::MuslF32:
110     return IsLA32 ? "ilp32f" : "lp64f";
111   case llvm::Triple::GNUF64:
112     // This was originally permitted (and indeed the canonical way) to
113     // represent the {ILP32,LP64}D ABIs, but in Feb 2023 Loongson decided to
114     // drop the explicit suffix in favor of unmarked `-gnu` for the
115     // "general-purpose" ABIs, among other non-technical reasons.
116     //
117     // The spec change did not mention whether existing usages of "gnuf64"
118     // shall remain valid or not, so we are going to continue recognizing it
119     // for some time, until it is clear that everyone else has migrated away
120     // from it.
121     [[fallthrough]];
122   case llvm::Triple::GNU:
123   default:
124     return IsLA32 ? "ilp32d" : "lp64d";
125   }
126 }
127 
getLoongArchTargetFeatures(const Driver & D,const llvm::Triple & Triple,const ArgList & Args,std::vector<StringRef> & Features)128 void loongarch::getLoongArchTargetFeatures(const Driver &D,
129                                            const llvm::Triple &Triple,
130                                            const ArgList &Args,
131                                            std::vector<StringRef> &Features) {
132   // Enable the `lsx` feature on 64-bit LoongArch by default.
133   if (Triple.isLoongArch64() &&
134       (!Args.hasArgNoClaim(clang::driver::options::OPT_march_EQ)))
135     Features.push_back("+lsx");
136 
137   // FIXME: Now we must use -mrelax to enable relax, maybe -mrelax will be set
138   // as default in the future.
139   if (const Arg *A =
140           Args.getLastArg(options::OPT_mrelax, options::OPT_mno_relax)) {
141     if (A->getOption().matches(options::OPT_mrelax)) {
142       Features.push_back("+relax");
143       // -gsplit-dwarf -mrelax requires DW_AT_high_pc/DW_AT_ranges/... indexing
144       // into .debug_addr, which is currently not implemented.
145       Arg *A;
146       if (getDebugFissionKind(D, Args, A) != DwarfFissionKind::None)
147         D.Diag(
148             clang::diag::err_drv_loongarch_unsupported_with_linker_relaxation)
149             << A->getAsString(Args);
150     } else {
151       Features.push_back("-relax");
152     }
153   }
154 
155   std::string ArchName;
156   const Arg *MArch = Args.getLastArg(options::OPT_march_EQ);
157   if (MArch)
158     ArchName = MArch->getValue();
159   ArchName = postProcessTargetCPUString(ArchName, Triple);
160   llvm::LoongArch::getArchFeatures(ArchName, Features);
161   if (MArch && StringRef(MArch->getValue()) == "native")
162     for (auto &F : llvm::sys::getHostCPUFeatures())
163       Features.push_back(
164           Args.MakeArgString((F.second ? "+" : "-") + F.first()));
165 
166   // Select floating-point features determined by -mdouble-float,
167   // -msingle-float, -msoft-float and -mfpu.
168   // Note: -m*-float wins any other options.
169   if (const Arg *A = Args.getLastArg(options::OPT_mdouble_float,
170                                      options::OPT_msingle_float,
171                                      options::OPT_msoft_float)) {
172     if (A->getOption().matches(options::OPT_mdouble_float)) {
173       Features.push_back("+f");
174       Features.push_back("+d");
175     } else if (A->getOption().matches(options::OPT_msingle_float)) {
176       Features.push_back("+f");
177       Features.push_back("-d");
178       Features.push_back("-lsx");
179     } else /*Soft-float*/ {
180       Features.push_back("-f");
181       Features.push_back("-d");
182       Features.push_back("-lsx");
183     }
184   } else if (const Arg *A = Args.getLastArg(options::OPT_mfpu_EQ)) {
185     StringRef FPU = A->getValue();
186     if (FPU == "64") {
187       Features.push_back("+f");
188       Features.push_back("+d");
189     } else if (FPU == "32") {
190       Features.push_back("+f");
191       Features.push_back("-d");
192       Features.push_back("-lsx");
193     } else if (FPU == "0" || FPU == "none") {
194       Features.push_back("-f");
195       Features.push_back("-d");
196       Features.push_back("-lsx");
197     } else {
198       D.Diag(diag::err_drv_loongarch_invalid_mfpu_EQ) << FPU;
199     }
200   }
201 
202   // Accept but warn about these TargetSpecific options.
203   if (Arg *A = Args.getLastArgNoClaim(options::OPT_mabi_EQ))
204     A->ignoreTargetSpecific();
205   if (Arg *A = Args.getLastArgNoClaim(options::OPT_mfpu_EQ))
206     A->ignoreTargetSpecific();
207   if (Arg *A = Args.getLastArgNoClaim(options::OPT_msimd_EQ))
208     A->ignoreTargetSpecific();
209 
210   // Select lsx/lasx feature determined by -msimd=.
211   // Option -msimd= precedes -m[no-]lsx and -m[no-]lasx.
212   if (const Arg *A = Args.getLastArg(options::OPT_msimd_EQ)) {
213     StringRef MSIMD = A->getValue();
214     if (MSIMD == "lsx") {
215       // Option -msimd=lsx depends on 64-bit FPU.
216       // -m*-float and -mfpu=none/0/32 conflict with -msimd=lsx.
217       if (llvm::is_contained(Features, "-d"))
218         D.Diag(diag::err_drv_loongarch_wrong_fpu_width) << /*LSX*/ 0;
219       else
220         Features.push_back("+lsx");
221     } else if (MSIMD == "lasx") {
222       // Option -msimd=lasx depends on 64-bit FPU and LSX.
223       // -m*-float, -mfpu=none/0/32 and -mno-lsx conflict with -msimd=lasx.
224       if (llvm::is_contained(Features, "-d"))
225         D.Diag(diag::err_drv_loongarch_wrong_fpu_width) << /*LASX*/ 1;
226       else if (llvm::is_contained(Features, "-lsx"))
227         D.Diag(diag::err_drv_loongarch_invalid_simd_option_combination);
228 
229       // The command options do not contain -mno-lasx.
230       if (!Args.getLastArg(options::OPT_mno_lasx)) {
231         Features.push_back("+lsx");
232         Features.push_back("+lasx");
233       }
234     } else if (MSIMD == "none") {
235       if (llvm::is_contained(Features, "+lsx"))
236         Features.push_back("-lsx");
237       if (llvm::is_contained(Features, "+lasx"))
238         Features.push_back("-lasx");
239     } else {
240       D.Diag(diag::err_drv_loongarch_invalid_msimd_EQ) << MSIMD;
241     }
242   }
243 
244   // Select lsx feature determined by -m[no-]lsx.
245   if (const Arg *A = Args.getLastArg(options::OPT_mlsx, options::OPT_mno_lsx)) {
246     // LSX depends on 64-bit FPU.
247     // -m*-float and -mfpu=none/0/32 conflict with -mlsx.
248     if (A->getOption().matches(options::OPT_mlsx)) {
249       if (llvm::find(Features, "-d") != Features.end())
250         D.Diag(diag::err_drv_loongarch_wrong_fpu_width) << /*LSX*/ 0;
251       else /*-mlsx*/
252         Features.push_back("+lsx");
253     } else /*-mno-lsx*/ {
254       Features.push_back("-lsx");
255       Features.push_back("-lasx");
256     }
257   }
258 
259   // Select lasx feature determined by -m[no-]lasx.
260   if (const Arg *A =
261           Args.getLastArg(options::OPT_mlasx, options::OPT_mno_lasx)) {
262     // LASX depends on 64-bit FPU and LSX.
263     // -mno-lsx conflicts with -mlasx.
264     if (A->getOption().matches(options::OPT_mlasx)) {
265       if (llvm::find(Features, "-d") != Features.end())
266         D.Diag(diag::err_drv_loongarch_wrong_fpu_width) << /*LASX*/ 1;
267       else { /*-mlasx*/
268         Features.push_back("+lsx");
269         Features.push_back("+lasx");
270       }
271     } else /*-mno-lasx*/
272       Features.push_back("-lasx");
273   }
274 
275   AddTargetFeature(Args, Features, options::OPT_mno_strict_align,
276                    options::OPT_mstrict_align, "ual");
277   AddTargetFeature(Args, Features, options::OPT_mno_strict_align,
278                    options::OPT_mstrict_align, "ual");
279   AddTargetFeature(Args, Features, options::OPT_mfrecipe,
280                    options::OPT_mno_frecipe, "frecipe");
281   AddTargetFeature(Args, Features, options::OPT_mlam_bh,
282                    options::OPT_mno_lam_bh, "lam-bh");
283   AddTargetFeature(Args, Features, options::OPT_mlamcas,
284                    options::OPT_mno_lamcas, "lamcas");
285   AddTargetFeature(Args, Features, options::OPT_mld_seq_sa,
286                    options::OPT_mno_ld_seq_sa, "ld-seq-sa");
287   AddTargetFeature(Args, Features, options::OPT_mdiv32,
288                    options::OPT_mno_div32, "div32");
289   AddTargetFeature(Args, Features, options::OPT_mscq, options::OPT_mno_scq,
290                    "scq");
291 }
292 
postProcessTargetCPUString(const std::string & CPU,const llvm::Triple & Triple)293 std::string loongarch::postProcessTargetCPUString(const std::string &CPU,
294                                                   const llvm::Triple &Triple) {
295   std::string CPUString = CPU;
296   if (CPUString == "native") {
297     CPUString = llvm::sys::getHostCPUName();
298     if (CPUString == "generic")
299       CPUString = llvm::LoongArch::getDefaultArch(Triple.isLoongArch64());
300   }
301   if (CPUString.empty())
302     CPUString = llvm::LoongArch::getDefaultArch(Triple.isLoongArch64());
303   return CPUString;
304 }
305 
getLoongArchTargetCPU(const llvm::opt::ArgList & Args,const llvm::Triple & Triple)306 std::string loongarch::getLoongArchTargetCPU(const llvm::opt::ArgList &Args,
307                                              const llvm::Triple &Triple) {
308   std::string CPU;
309   std::string Arch;
310   // If we have -march, use that.
311   if (const Arg *A = Args.getLastArg(options::OPT_march_EQ)) {
312     Arch = A->getValue();
313     if (Arch == "la64v1.0" || Arch == "la64v1.1")
314       CPU = llvm::LoongArch::getDefaultArch(Triple.isLoongArch64());
315     else
316       CPU = Arch;
317   }
318   return postProcessTargetCPUString(CPU, Triple);
319 }
320