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"
10#include "../Clang.h"
13#include "clang/Driver/Driver.h"
16#include "llvm/TargetParser/Host.h"
17#include "llvm/TargetParser/LoongArchTargetParser.h"
18
19using namespace clang::driver;
20using namespace clang::driver::tools;
21using namespace clang;
22using namespace llvm::opt;
23
24StringRef 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
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
293std::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
306std::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}
#define V(N, I)
Definition: ASTContext.h:3460
const Decl * D
Driver - Encapsulate logic for constructing compilation processes from a set of gcc-driver-like comma...
Definition: Driver.h:99
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:306
std::string postProcessTargetCPUString(const std::string &CPU, const llvm::Triple &Triple)
Definition: LoongArch.cpp:293
void getLoongArchTargetFeatures(const Driver &D, const llvm::Triple &Triple, const llvm::opt::ArgList &Args, std::vector< llvm::StringRef > &Features)
DwarfFissionKind getDebugFissionKind(const Driver &D, const llvm::opt::ArgList &Args, llvm::opt::Arg *&Arg)
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.