clang 20.0.0git
LoongArch.cpp
Go to the documentation of this file.
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"
12#include "clang/Driver/Driver.h"
15#include "llvm/TargetParser/Host.h"
16#include "llvm/TargetParser/LoongArchTargetParser.h"
17
18using namespace clang::driver;
19using namespace clang::driver::tools;
20using namespace clang;
21using namespace llvm::opt;
22
23StringRef 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
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 std::string ArchName;
138 const Arg *MArch = Args.getLastArg(options::OPT_march_EQ);
139 if (MArch)
140 ArchName = MArch->getValue();
141 ArchName = postProcessTargetCPUString(ArchName, Triple);
142 llvm::LoongArch::getArchFeatures(ArchName, Features);
143 if (MArch && StringRef(MArch->getValue()) == "native")
144 for (auto &F : llvm::sys::getHostCPUFeatures())
145 Features.push_back(
146 Args.MakeArgString((F.second ? "+" : "-") + F.first()));
147
148 // Select floating-point features determined by -mdouble-float,
149 // -msingle-float, -msoft-float and -mfpu.
150 // Note: -m*-float wins any other options.
151 if (const Arg *A = Args.getLastArg(options::OPT_mdouble_float,
152 options::OPT_msingle_float,
153 options::OPT_msoft_float)) {
154 if (A->getOption().matches(options::OPT_mdouble_float)) {
155 Features.push_back("+f");
156 Features.push_back("+d");
157 } else if (A->getOption().matches(options::OPT_msingle_float)) {
158 Features.push_back("+f");
159 Features.push_back("-d");
160 Features.push_back("-lsx");
161 } else /*Soft-float*/ {
162 Features.push_back("-f");
163 Features.push_back("-d");
164 Features.push_back("-lsx");
165 }
166 } else if (const Arg *A = Args.getLastArg(options::OPT_mfpu_EQ)) {
167 StringRef FPU = A->getValue();
168 if (FPU == "64") {
169 Features.push_back("+f");
170 Features.push_back("+d");
171 } else if (FPU == "32") {
172 Features.push_back("+f");
173 Features.push_back("-d");
174 Features.push_back("-lsx");
175 } else if (FPU == "0" || FPU == "none") {
176 Features.push_back("-f");
177 Features.push_back("-d");
178 Features.push_back("-lsx");
179 } else {
180 D.Diag(diag::err_drv_loongarch_invalid_mfpu_EQ) << FPU;
181 }
182 }
183
184 // Select the `ual` feature determined by -m[no-]strict-align.
185 AddTargetFeature(Args, Features, options::OPT_mno_strict_align,
186 options::OPT_mstrict_align, "ual");
187
188 // Accept but warn about these TargetSpecific options.
189 if (Arg *A = Args.getLastArgNoClaim(options::OPT_mabi_EQ))
190 A->ignoreTargetSpecific();
191 if (Arg *A = Args.getLastArgNoClaim(options::OPT_mfpu_EQ))
192 A->ignoreTargetSpecific();
193 if (Arg *A = Args.getLastArgNoClaim(options::OPT_msimd_EQ))
194 A->ignoreTargetSpecific();
195
196 // Select lsx/lasx feature determined by -msimd=.
197 // Option -msimd= precedes -m[no-]lsx and -m[no-]lasx.
198 if (const Arg *A = Args.getLastArg(options::OPT_msimd_EQ)) {
199 StringRef MSIMD = A->getValue();
200 if (MSIMD == "lsx") {
201 // Option -msimd=lsx depends on 64-bit FPU.
202 // -m*-float and -mfpu=none/0/32 conflict with -msimd=lsx.
203 if (llvm::find(Features, "-d") != Features.end())
204 D.Diag(diag::err_drv_loongarch_wrong_fpu_width) << /*LSX*/ 0;
205 else
206 Features.push_back("+lsx");
207 } else if (MSIMD == "lasx") {
208 // Option -msimd=lasx depends on 64-bit FPU and LSX.
209 // -m*-float, -mfpu=none/0/32 and -mno-lsx conflict with -msimd=lasx.
210 if (llvm::find(Features, "-d") != Features.end())
211 D.Diag(diag::err_drv_loongarch_wrong_fpu_width) << /*LASX*/ 1;
212 else if (llvm::find(Features, "-lsx") != Features.end())
213 D.Diag(diag::err_drv_loongarch_invalid_simd_option_combination);
214
215 // The command options do not contain -mno-lasx.
216 if (!Args.getLastArg(options::OPT_mno_lasx)) {
217 Features.push_back("+lsx");
218 Features.push_back("+lasx");
219 }
220 } else if (MSIMD == "none") {
221 if (llvm::find(Features, "+lsx") != Features.end())
222 Features.push_back("-lsx");
223 if (llvm::find(Features, "+lasx") != Features.end())
224 Features.push_back("-lasx");
225 } else {
226 D.Diag(diag::err_drv_loongarch_invalid_msimd_EQ) << MSIMD;
227 }
228 }
229
230 // Select lsx feature determined by -m[no-]lsx.
231 if (const Arg *A = Args.getLastArg(options::OPT_mlsx, options::OPT_mno_lsx)) {
232 // LSX depends on 64-bit FPU.
233 // -m*-float and -mfpu=none/0/32 conflict with -mlsx.
234 if (A->getOption().matches(options::OPT_mlsx)) {
235 if (llvm::find(Features, "-d") != Features.end())
236 D.Diag(diag::err_drv_loongarch_wrong_fpu_width) << /*LSX*/ 0;
237 else /*-mlsx*/
238 Features.push_back("+lsx");
239 } else /*-mno-lsx*/ {
240 Features.push_back("-lsx");
241 }
242 }
243
244 // Select lasx feature determined by -m[no-]lasx.
245 if (const Arg *A =
246 Args.getLastArg(options::OPT_mlasx, options::OPT_mno_lasx)) {
247 // LASX depends on 64-bit FPU and LSX.
248 // -mno-lsx conflicts with -mlasx.
249 if (A->getOption().matches(options::OPT_mlasx)) {
250 if (llvm::find(Features, "-d") != Features.end())
251 D.Diag(diag::err_drv_loongarch_wrong_fpu_width) << /*LASX*/ 1;
252 else { /*-mlasx*/
253 Features.push_back("+lsx");
254 Features.push_back("+lasx");
255 }
256 } else /*-mno-lasx*/
257 Features.push_back("-lasx");
258 }
259
260 // Select frecipe feature determined by -m[no-]frecipe.
261 if (const Arg *A =
262 Args.getLastArg(options::OPT_mfrecipe, options::OPT_mno_frecipe)) {
263 if (A->getOption().matches(options::OPT_mfrecipe))
264 Features.push_back("+frecipe");
265 else
266 Features.push_back("-frecipe");
267 }
268
269 // Select lam-bh feature determined by -m[no-]lam-bh.
270 if (const Arg *A =
271 Args.getLastArg(options::OPT_mlam_bh, options::OPT_mno_lam_bh)) {
272 if (A->getOption().matches(options::OPT_mlam_bh))
273 Features.push_back("+lam-bh");
274 else
275 Features.push_back("-lam-bh");
276 }
277
278 // Select lamcas feature determined by -m[no-]lamcas.
279 if (const Arg *A =
280 Args.getLastArg(options::OPT_mlamcas, options::OPT_mno_lamcas)) {
281 if (A->getOption().matches(options::OPT_mlamcas))
282 Features.push_back("+lamcas");
283 else
284 Features.push_back("-lamcas");
285 }
286
287 // Select ld-seq-sa feature determined by -m[no-]ld-seq-sa.
288 if (const Arg *A = Args.getLastArg(options::OPT_mld_seq_sa,
289 options::OPT_mno_ld_seq_sa)) {
290 if (A->getOption().matches(options::OPT_mld_seq_sa))
291 Features.push_back("+ld-seq-sa");
292 else
293 Features.push_back("-ld-seq-sa");
294 }
295
296 // Select div32 feature determined by -m[no-]div32.
297 if (const Arg *A =
298 Args.getLastArg(options::OPT_mdiv32, options::OPT_mno_div32)) {
299 if (A->getOption().matches(options::OPT_mdiv32))
300 Features.push_back("+div32");
301 else
302 Features.push_back("-div32");
303 }
304}
305
306std::string loongarch::postProcessTargetCPUString(const std::string &CPU,
307 const llvm::Triple &Triple) {
308 std::string CPUString = CPU;
309 if (CPUString == "native") {
310 CPUString = llvm::sys::getHostCPUName();
311 if (CPUString == "generic")
312 CPUString = llvm::LoongArch::getDefaultArch(Triple.isLoongArch64());
313 }
314 if (CPUString.empty())
315 CPUString = llvm::LoongArch::getDefaultArch(Triple.isLoongArch64());
316 return CPUString;
317}
318
319std::string loongarch::getLoongArchTargetCPU(const llvm::opt::ArgList &Args,
320 const llvm::Triple &Triple) {
321 std::string CPU;
322 std::string Arch;
323 // If we have -march, use that.
324 if (const Arg *A = Args.getLastArg(options::OPT_march_EQ)) {
325 Arch = A->getValue();
326 if (Arch == "la64v1.0" || Arch == "la64v1.1")
327 CPU = llvm::LoongArch::getDefaultArch(Triple.isLoongArch64());
328 else
329 CPU = Arch;
330 }
331 return postProcessTargetCPUString(CPU, Triple);
332}
#define V(N, I)
Definition: ASTContext.h:3443
const Decl * D
Driver - Encapsulate logic for constructing compilation processes from a set of gcc-driver-like comma...
Definition: Driver.h:77
StringRef getLoongArchABI(const Driver &D, const llvm::opt::ArgList &Args, const llvm::Triple &Triple)
std::string getLoongArchTargetCPU(const llvm::opt::ArgList &Args, const llvm::Triple &Triple)
Definition: LoongArch.cpp:319
std::string postProcessTargetCPUString(const std::string &CPU, const llvm::Triple &Triple)
Definition: LoongArch.cpp:306
void getLoongArchTargetFeatures(const Driver &D, const llvm::Triple &Triple, const llvm::opt::ArgList &Args, std::vector< llvm::StringRef > &Features)
void AddTargetFeature(const llvm::opt::ArgList &Args, std::vector< StringRef > &Features, llvm::opt::OptSpecifier OnOpt, llvm::opt::OptSpecifier OffOpt, StringRef FeatureName)
The JSON file list parser is used to communicate input to InstallAPI.