xref: /freebsd/contrib/llvm-project/clang/lib/Driver/ToolChains/Arch/LoongArch.cpp (revision 7937bfbc0ca53fe7cdd0d54414f9296e273a518e)
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 "ToolChains/CommonArgs.h"
11 #include "clang/Basic/DiagnosticDriver.h"
12 #include "clang/Driver/Driver.h"
13 #include "clang/Driver/DriverDiagnostic.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 
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     return IsLA32 ? "ilp32s" : "lp64s";
107   case llvm::Triple::GNUF32:
108     return IsLA32 ? "ilp32f" : "lp64f";
109   case llvm::Triple::GNUF64:
110     // This was originally permitted (and indeed the canonical way) to
111     // represent the {ILP32,LP64}D ABIs, but in Feb 2023 Loongson decided to
112     // drop the explicit suffix in favor of unmarked `-gnu` for the
113     // "general-purpose" ABIs, among other non-technical reasons.
114     //
115     // The spec change did not mention whether existing usages of "gnuf64"
116     // shall remain valid or not, so we are going to continue recognizing it
117     // for some time, until it is clear that everyone else has migrated away
118     // from it.
119     [[fallthrough]];
120   case llvm::Triple::GNU:
121   default:
122     return IsLA32 ? "ilp32d" : "lp64d";
123   }
124 }
125 
126 void loongarch::getLoongArchTargetFeatures(const Driver &D,
127                                            const llvm::Triple &Triple,
128                                            const ArgList &Args,
129                                            std::vector<StringRef> &Features) {
130   // Enable the `lsx` feature on 64-bit LoongArch by default.
131   if (Triple.isLoongArch64() &&
132       (!Args.hasArgNoClaim(clang::driver::options::OPT_march_EQ)))
133     Features.push_back("+lsx");
134 
135   std::string ArchName;
136   if (const Arg *A = Args.getLastArg(options::OPT_march_EQ))
137     ArchName = A->getValue();
138   ArchName = postProcessTargetCPUString(ArchName, Triple);
139   llvm::LoongArch::getArchFeatures(ArchName, Features);
140 
141   // Select floating-point features determined by -mdouble-float,
142   // -msingle-float, -msoft-float and -mfpu.
143   // Note: -m*-float wins any other options.
144   if (const Arg *A = Args.getLastArg(options::OPT_mdouble_float,
145                                      options::OPT_msingle_float,
146                                      options::OPT_msoft_float)) {
147     if (A->getOption().matches(options::OPT_mdouble_float)) {
148       Features.push_back("+f");
149       Features.push_back("+d");
150     } else if (A->getOption().matches(options::OPT_msingle_float)) {
151       Features.push_back("+f");
152       Features.push_back("-d");
153       Features.push_back("-lsx");
154     } else /*Soft-float*/ {
155       Features.push_back("-f");
156       Features.push_back("-d");
157       Features.push_back("-lsx");
158     }
159   } else if (const Arg *A = Args.getLastArg(options::OPT_mfpu_EQ)) {
160     StringRef FPU = A->getValue();
161     if (FPU == "64") {
162       Features.push_back("+f");
163       Features.push_back("+d");
164     } else if (FPU == "32") {
165       Features.push_back("+f");
166       Features.push_back("-d");
167       Features.push_back("-lsx");
168     } else if (FPU == "0" || FPU == "none") {
169       Features.push_back("-f");
170       Features.push_back("-d");
171       Features.push_back("-lsx");
172     } else {
173       D.Diag(diag::err_drv_loongarch_invalid_mfpu_EQ) << FPU;
174     }
175   }
176 
177   // Select the `ual` feature determined by -m[no-]strict-align.
178   AddTargetFeature(Args, Features, options::OPT_mno_strict_align,
179                    options::OPT_mstrict_align, "ual");
180 
181   // Accept but warn about these TargetSpecific options.
182   if (Arg *A = Args.getLastArgNoClaim(options::OPT_mabi_EQ))
183     A->ignoreTargetSpecific();
184   if (Arg *A = Args.getLastArgNoClaim(options::OPT_mfpu_EQ))
185     A->ignoreTargetSpecific();
186   if (Arg *A = Args.getLastArgNoClaim(options::OPT_msimd_EQ))
187     A->ignoreTargetSpecific();
188 
189   // Select lsx/lasx feature determined by -msimd=.
190   // Option -msimd= precedes -m[no-]lsx and -m[no-]lasx.
191   if (const Arg *A = Args.getLastArg(options::OPT_msimd_EQ)) {
192     StringRef MSIMD = A->getValue();
193     if (MSIMD == "lsx") {
194       // Option -msimd=lsx depends on 64-bit FPU.
195       // -m*-float and -mfpu=none/0/32 conflict with -msimd=lsx.
196       if (llvm::find(Features, "-d") != Features.end())
197         D.Diag(diag::err_drv_loongarch_wrong_fpu_width) << /*LSX*/ 0;
198       else
199         Features.push_back("+lsx");
200     } else if (MSIMD == "lasx") {
201       // Option -msimd=lasx depends on 64-bit FPU and LSX.
202       // -m*-float, -mfpu=none/0/32 and -mno-lsx conflict with -msimd=lasx.
203       if (llvm::find(Features, "-d") != Features.end())
204         D.Diag(diag::err_drv_loongarch_wrong_fpu_width) << /*LASX*/ 1;
205       else if (llvm::find(Features, "-lsx") != Features.end())
206         D.Diag(diag::err_drv_loongarch_invalid_simd_option_combination);
207 
208       // The command options do not contain -mno-lasx.
209       if (!Args.getLastArg(options::OPT_mno_lasx)) {
210         Features.push_back("+lsx");
211         Features.push_back("+lasx");
212       }
213     } else if (MSIMD == "none") {
214       if (llvm::find(Features, "+lsx") != Features.end())
215         Features.push_back("-lsx");
216       if (llvm::find(Features, "+lasx") != Features.end())
217         Features.push_back("-lasx");
218     } else {
219       D.Diag(diag::err_drv_loongarch_invalid_msimd_EQ) << MSIMD;
220     }
221   }
222 
223   // Select lsx feature determined by -m[no-]lsx.
224   if (const Arg *A = Args.getLastArg(options::OPT_mlsx, options::OPT_mno_lsx)) {
225     // LSX depends on 64-bit FPU.
226     // -m*-float and -mfpu=none/0/32 conflict with -mlsx.
227     if (A->getOption().matches(options::OPT_mlsx)) {
228       if (llvm::find(Features, "-d") != Features.end())
229         D.Diag(diag::err_drv_loongarch_wrong_fpu_width) << /*LSX*/ 0;
230       else /*-mlsx*/
231         Features.push_back("+lsx");
232     } else /*-mno-lsx*/ {
233       Features.push_back("-lsx");
234     }
235   }
236 
237   // Select lasx feature determined by -m[no-]lasx.
238   if (const Arg *A =
239           Args.getLastArg(options::OPT_mlasx, options::OPT_mno_lasx)) {
240     // LASX depends on 64-bit FPU and LSX.
241     // -mno-lsx conflicts with -mlasx.
242     if (A->getOption().matches(options::OPT_mlasx)) {
243       if (llvm::find(Features, "-d") != Features.end())
244         D.Diag(diag::err_drv_loongarch_wrong_fpu_width) << /*LASX*/ 1;
245       else { /*-mlasx*/
246         Features.push_back("+lsx");
247         Features.push_back("+lasx");
248       }
249     } else /*-mno-lasx*/
250       Features.push_back("-lasx");
251   }
252 }
253 
254 std::string loongarch::postProcessTargetCPUString(const std::string &CPU,
255                                                   const llvm::Triple &Triple) {
256   std::string CPUString = CPU;
257   if (CPUString == "native") {
258     CPUString = llvm::sys::getHostCPUName();
259     if (CPUString == "generic")
260       CPUString = llvm::LoongArch::getDefaultArch(Triple.isLoongArch64());
261   }
262   if (CPUString.empty())
263     CPUString = llvm::LoongArch::getDefaultArch(Triple.isLoongArch64());
264   return CPUString;
265 }
266 
267 std::string loongarch::getLoongArchTargetCPU(const llvm::opt::ArgList &Args,
268                                              const llvm::Triple &Triple) {
269   std::string CPU;
270   std::string Arch;
271   // If we have -march, use that.
272   if (const Arg *A = Args.getLastArg(options::OPT_march_EQ)) {
273     Arch = A->getValue();
274     if (Arch == "la64v1.0" || Arch == "la64v1.1")
275       CPU = llvm::LoongArch::getDefaultArch(Triple.isLoongArch64());
276     else
277       CPU = Arch;
278   }
279   return postProcessTargetCPUString(CPU, Triple);
280 }
281