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