xref: /freebsd-src/contrib/llvm-project/clang/lib/Driver/ToolChains/Arch/LoongArch.cpp (revision 0fca6ea1d4eea4c934cfff25ac9ee8ad6fe95583)
1bdd1243dSDimitry Andric //===--- LoongArch.cpp - LoongArch Helpers for Tools ------------*- C++ -*-===//
2bdd1243dSDimitry Andric //
3bdd1243dSDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4bdd1243dSDimitry Andric // See https://llvm.org/LICENSE.txt for license information.
5bdd1243dSDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6bdd1243dSDimitry Andric //
7bdd1243dSDimitry Andric //===----------------------------------------------------------------------===//
8bdd1243dSDimitry Andric 
9bdd1243dSDimitry Andric #include "LoongArch.h"
1006c3fb27SDimitry Andric #include "ToolChains/CommonArgs.h"
11bdd1243dSDimitry Andric #include "clang/Basic/DiagnosticDriver.h"
12bdd1243dSDimitry Andric #include "clang/Driver/Driver.h"
13bdd1243dSDimitry Andric #include "clang/Driver/DriverDiagnostic.h"
14bdd1243dSDimitry Andric #include "clang/Driver/Options.h"
158a4dda33SDimitry Andric #include "llvm/TargetParser/Host.h"
1606c3fb27SDimitry Andric #include "llvm/TargetParser/LoongArchTargetParser.h"
17bdd1243dSDimitry Andric 
18bdd1243dSDimitry Andric using namespace clang::driver;
19bdd1243dSDimitry Andric using namespace clang::driver::tools;
20bdd1243dSDimitry Andric using namespace clang;
21bdd1243dSDimitry Andric using namespace llvm::opt;
22bdd1243dSDimitry Andric 
23bdd1243dSDimitry Andric StringRef loongarch::getLoongArchABI(const Driver &D, const ArgList &Args,
24bdd1243dSDimitry Andric                                      const llvm::Triple &Triple) {
25bdd1243dSDimitry Andric   assert((Triple.getArch() == llvm::Triple::loongarch32 ||
26bdd1243dSDimitry Andric           Triple.getArch() == llvm::Triple::loongarch64) &&
27bdd1243dSDimitry Andric          "Unexpected triple");
28bdd1243dSDimitry Andric   bool IsLA32 = Triple.getArch() == llvm::Triple::loongarch32;
29bdd1243dSDimitry Andric 
3006c3fb27SDimitry Andric   // Record -mabi value for later use.
3106c3fb27SDimitry Andric   const Arg *MABIArg = Args.getLastArg(options::OPT_mabi_EQ);
3206c3fb27SDimitry Andric   StringRef MABIValue;
3306c3fb27SDimitry Andric   if (MABIArg) {
3406c3fb27SDimitry Andric     MABIValue = MABIArg->getValue();
3506c3fb27SDimitry Andric   }
3606c3fb27SDimitry Andric 
3706c3fb27SDimitry Andric   // Parse -mfpu value for later use.
3806c3fb27SDimitry Andric   const Arg *MFPUArg = Args.getLastArg(options::OPT_mfpu_EQ);
3906c3fb27SDimitry Andric   int FPU = -1;
4006c3fb27SDimitry Andric   if (MFPUArg) {
4106c3fb27SDimitry Andric     StringRef V = MFPUArg->getValue();
4206c3fb27SDimitry Andric     if (V == "64")
4306c3fb27SDimitry Andric       FPU = 64;
4406c3fb27SDimitry Andric     else if (V == "32")
4506c3fb27SDimitry Andric       FPU = 32;
4606c3fb27SDimitry Andric     else if (V == "0" || V == "none")
4706c3fb27SDimitry Andric       FPU = 0;
4806c3fb27SDimitry Andric     else
4906c3fb27SDimitry Andric       D.Diag(diag::err_drv_loongarch_invalid_mfpu_EQ) << V;
5006c3fb27SDimitry Andric   }
5106c3fb27SDimitry Andric 
52bdd1243dSDimitry Andric   // Check -m*-float firstly since they have highest priority.
53bdd1243dSDimitry Andric   if (const Arg *A = Args.getLastArg(options::OPT_mdouble_float,
54bdd1243dSDimitry Andric                                      options::OPT_msingle_float,
55bdd1243dSDimitry Andric                                      options::OPT_msoft_float)) {
5606c3fb27SDimitry Andric     StringRef ImpliedABI;
5706c3fb27SDimitry Andric     int ImpliedFPU = -1;
5806c3fb27SDimitry Andric     if (A->getOption().matches(options::OPT_mdouble_float)) {
5906c3fb27SDimitry Andric       ImpliedABI = IsLA32 ? "ilp32d" : "lp64d";
6006c3fb27SDimitry Andric       ImpliedFPU = 64;
6106c3fb27SDimitry Andric     }
6206c3fb27SDimitry Andric     if (A->getOption().matches(options::OPT_msingle_float)) {
6306c3fb27SDimitry Andric       ImpliedABI = IsLA32 ? "ilp32f" : "lp64f";
6406c3fb27SDimitry Andric       ImpliedFPU = 32;
6506c3fb27SDimitry Andric     }
6606c3fb27SDimitry Andric     if (A->getOption().matches(options::OPT_msoft_float)) {
6706c3fb27SDimitry Andric       ImpliedABI = IsLA32 ? "ilp32s" : "lp64s";
6806c3fb27SDimitry Andric       ImpliedFPU = 0;
6906c3fb27SDimitry Andric     }
7006c3fb27SDimitry Andric 
7106c3fb27SDimitry Andric     // Check `-mabi=` and `-mfpu=` settings and report if they conflict with
7206c3fb27SDimitry Andric     // the higher-priority settings implied by -m*-float.
7306c3fb27SDimitry Andric     //
7406c3fb27SDimitry Andric     // ImpliedABI and ImpliedFPU are guaranteed to have valid values because
7506c3fb27SDimitry Andric     // one of the match arms must match if execution can arrive here at all.
7606c3fb27SDimitry Andric     if (!MABIValue.empty() && ImpliedABI != MABIValue)
7706c3fb27SDimitry Andric       D.Diag(diag::warn_drv_loongarch_conflicting_implied_val)
7806c3fb27SDimitry Andric           << MABIArg->getAsString(Args) << A->getAsString(Args) << ImpliedABI;
7906c3fb27SDimitry Andric 
8006c3fb27SDimitry Andric     if (FPU != -1 && ImpliedFPU != FPU)
8106c3fb27SDimitry Andric       D.Diag(diag::warn_drv_loongarch_conflicting_implied_val)
8206c3fb27SDimitry Andric           << MFPUArg->getAsString(Args) << A->getAsString(Args) << ImpliedFPU;
8306c3fb27SDimitry Andric 
8406c3fb27SDimitry Andric     return ImpliedABI;
85bdd1243dSDimitry Andric   }
86bdd1243dSDimitry Andric 
87bdd1243dSDimitry Andric   // If `-mabi=` is specified, use it.
8806c3fb27SDimitry Andric   if (!MABIValue.empty())
8906c3fb27SDimitry Andric     return MABIValue;
90bdd1243dSDimitry Andric 
91bdd1243dSDimitry Andric   // Select abi based on -mfpu=xx.
9206c3fb27SDimitry Andric   switch (FPU) {
9306c3fb27SDimitry Andric   case 64:
94bdd1243dSDimitry Andric     return IsLA32 ? "ilp32d" : "lp64d";
9506c3fb27SDimitry Andric   case 32:
96bdd1243dSDimitry Andric     return IsLA32 ? "ilp32f" : "lp64f";
9706c3fb27SDimitry Andric   case 0:
98bdd1243dSDimitry Andric     return IsLA32 ? "ilp32s" : "lp64s";
99bdd1243dSDimitry Andric   }
100bdd1243dSDimitry Andric 
101bdd1243dSDimitry Andric   // Choose a default based on the triple.
10206c3fb27SDimitry Andric   // Honor the explicit ABI modifier suffix in triple's environment part if
10306c3fb27SDimitry Andric   // present, falling back to {ILP32,LP64}D otherwise.
10406c3fb27SDimitry Andric   switch (Triple.getEnvironment()) {
10506c3fb27SDimitry Andric   case llvm::Triple::GNUSF:
10606c3fb27SDimitry Andric     return IsLA32 ? "ilp32s" : "lp64s";
10706c3fb27SDimitry Andric   case llvm::Triple::GNUF32:
10806c3fb27SDimitry Andric     return IsLA32 ? "ilp32f" : "lp64f";
10906c3fb27SDimitry Andric   case llvm::Triple::GNUF64:
11006c3fb27SDimitry Andric     // This was originally permitted (and indeed the canonical way) to
11106c3fb27SDimitry Andric     // represent the {ILP32,LP64}D ABIs, but in Feb 2023 Loongson decided to
11206c3fb27SDimitry Andric     // drop the explicit suffix in favor of unmarked `-gnu` for the
11306c3fb27SDimitry Andric     // "general-purpose" ABIs, among other non-technical reasons.
11406c3fb27SDimitry Andric     //
11506c3fb27SDimitry Andric     // The spec change did not mention whether existing usages of "gnuf64"
11606c3fb27SDimitry Andric     // shall remain valid or not, so we are going to continue recognizing it
11706c3fb27SDimitry Andric     // for some time, until it is clear that everyone else has migrated away
11806c3fb27SDimitry Andric     // from it.
11906c3fb27SDimitry Andric     [[fallthrough]];
12006c3fb27SDimitry Andric   case llvm::Triple::GNU:
12106c3fb27SDimitry Andric   default:
122bdd1243dSDimitry Andric     return IsLA32 ? "ilp32d" : "lp64d";
123bdd1243dSDimitry Andric   }
12406c3fb27SDimitry Andric }
125bdd1243dSDimitry Andric 
126bdd1243dSDimitry Andric void loongarch::getLoongArchTargetFeatures(const Driver &D,
127bdd1243dSDimitry Andric                                            const llvm::Triple &Triple,
128bdd1243dSDimitry Andric                                            const ArgList &Args,
129bdd1243dSDimitry Andric                                            std::vector<StringRef> &Features) {
130*0fca6ea1SDimitry Andric   // Enable the `lsx` feature on 64-bit LoongArch by default.
131*0fca6ea1SDimitry Andric   if (Triple.isLoongArch64() &&
132*0fca6ea1SDimitry Andric       (!Args.hasArgNoClaim(clang::driver::options::OPT_march_EQ)))
133*0fca6ea1SDimitry Andric     Features.push_back("+lsx");
134*0fca6ea1SDimitry Andric 
1358a4dda33SDimitry Andric   std::string ArchName;
1368a4dda33SDimitry Andric   if (const Arg *A = Args.getLastArg(options::OPT_march_EQ))
137bdd1243dSDimitry Andric     ArchName = A->getValue();
1388a4dda33SDimitry Andric   ArchName = postProcessTargetCPUString(ArchName, Triple);
139bdd1243dSDimitry Andric   llvm::LoongArch::getArchFeatures(ArchName, Features);
140bdd1243dSDimitry Andric 
141bdd1243dSDimitry Andric   // Select floating-point features determined by -mdouble-float,
142bdd1243dSDimitry Andric   // -msingle-float, -msoft-float and -mfpu.
143bdd1243dSDimitry Andric   // Note: -m*-float wins any other options.
144bdd1243dSDimitry Andric   if (const Arg *A = Args.getLastArg(options::OPT_mdouble_float,
145bdd1243dSDimitry Andric                                      options::OPT_msingle_float,
146bdd1243dSDimitry Andric                                      options::OPT_msoft_float)) {
147bdd1243dSDimitry Andric     if (A->getOption().matches(options::OPT_mdouble_float)) {
148bdd1243dSDimitry Andric       Features.push_back("+f");
149bdd1243dSDimitry Andric       Features.push_back("+d");
150bdd1243dSDimitry Andric     } else if (A->getOption().matches(options::OPT_msingle_float)) {
151bdd1243dSDimitry Andric       Features.push_back("+f");
152bdd1243dSDimitry Andric       Features.push_back("-d");
153*0fca6ea1SDimitry Andric       Features.push_back("-lsx");
154bdd1243dSDimitry Andric     } else /*Soft-float*/ {
155bdd1243dSDimitry Andric       Features.push_back("-f");
156bdd1243dSDimitry Andric       Features.push_back("-d");
157*0fca6ea1SDimitry Andric       Features.push_back("-lsx");
158bdd1243dSDimitry Andric     }
159bdd1243dSDimitry Andric   } else if (const Arg *A = Args.getLastArg(options::OPT_mfpu_EQ)) {
160bdd1243dSDimitry Andric     StringRef FPU = A->getValue();
161bdd1243dSDimitry Andric     if (FPU == "64") {
162bdd1243dSDimitry Andric       Features.push_back("+f");
163bdd1243dSDimitry Andric       Features.push_back("+d");
164bdd1243dSDimitry Andric     } else if (FPU == "32") {
165bdd1243dSDimitry Andric       Features.push_back("+f");
166bdd1243dSDimitry Andric       Features.push_back("-d");
167*0fca6ea1SDimitry Andric       Features.push_back("-lsx");
168bdd1243dSDimitry Andric     } else if (FPU == "0" || FPU == "none") {
169bdd1243dSDimitry Andric       Features.push_back("-f");
170bdd1243dSDimitry Andric       Features.push_back("-d");
171*0fca6ea1SDimitry Andric       Features.push_back("-lsx");
172bdd1243dSDimitry Andric     } else {
173bdd1243dSDimitry Andric       D.Diag(diag::err_drv_loongarch_invalid_mfpu_EQ) << FPU;
174bdd1243dSDimitry Andric     }
175bdd1243dSDimitry Andric   }
17606c3fb27SDimitry Andric 
177*0fca6ea1SDimitry Andric   // Select the `ual` feature determined by -m[no-]strict-align.
178*0fca6ea1SDimitry Andric   AddTargetFeature(Args, Features, options::OPT_mno_strict_align,
179*0fca6ea1SDimitry Andric                    options::OPT_mstrict_align, "ual");
18006c3fb27SDimitry Andric 
18106c3fb27SDimitry Andric   // Accept but warn about these TargetSpecific options.
18206c3fb27SDimitry Andric   if (Arg *A = Args.getLastArgNoClaim(options::OPT_mabi_EQ))
18306c3fb27SDimitry Andric     A->ignoreTargetSpecific();
18406c3fb27SDimitry Andric   if (Arg *A = Args.getLastArgNoClaim(options::OPT_mfpu_EQ))
18506c3fb27SDimitry Andric     A->ignoreTargetSpecific();
186*0fca6ea1SDimitry Andric   if (Arg *A = Args.getLastArgNoClaim(options::OPT_msimd_EQ))
187*0fca6ea1SDimitry Andric     A->ignoreTargetSpecific();
188*0fca6ea1SDimitry Andric 
189*0fca6ea1SDimitry Andric   // Select lsx/lasx feature determined by -msimd=.
190*0fca6ea1SDimitry Andric   // Option -msimd= precedes -m[no-]lsx and -m[no-]lasx.
191*0fca6ea1SDimitry Andric   if (const Arg *A = Args.getLastArg(options::OPT_msimd_EQ)) {
192*0fca6ea1SDimitry Andric     StringRef MSIMD = A->getValue();
193*0fca6ea1SDimitry Andric     if (MSIMD == "lsx") {
194*0fca6ea1SDimitry Andric       // Option -msimd=lsx depends on 64-bit FPU.
195*0fca6ea1SDimitry Andric       // -m*-float and -mfpu=none/0/32 conflict with -msimd=lsx.
196*0fca6ea1SDimitry Andric       if (llvm::find(Features, "-d") != Features.end())
197*0fca6ea1SDimitry Andric         D.Diag(diag::err_drv_loongarch_wrong_fpu_width) << /*LSX*/ 0;
198*0fca6ea1SDimitry Andric       else
199*0fca6ea1SDimitry Andric         Features.push_back("+lsx");
200*0fca6ea1SDimitry Andric     } else if (MSIMD == "lasx") {
201*0fca6ea1SDimitry Andric       // Option -msimd=lasx depends on 64-bit FPU and LSX.
202*0fca6ea1SDimitry Andric       // -m*-float, -mfpu=none/0/32 and -mno-lsx conflict with -msimd=lasx.
203*0fca6ea1SDimitry Andric       if (llvm::find(Features, "-d") != Features.end())
204*0fca6ea1SDimitry Andric         D.Diag(diag::err_drv_loongarch_wrong_fpu_width) << /*LASX*/ 1;
205*0fca6ea1SDimitry Andric       else if (llvm::find(Features, "-lsx") != Features.end())
206*0fca6ea1SDimitry Andric         D.Diag(diag::err_drv_loongarch_invalid_simd_option_combination);
207*0fca6ea1SDimitry Andric 
208*0fca6ea1SDimitry Andric       // The command options do not contain -mno-lasx.
209*0fca6ea1SDimitry Andric       if (!Args.getLastArg(options::OPT_mno_lasx)) {
210*0fca6ea1SDimitry Andric         Features.push_back("+lsx");
211*0fca6ea1SDimitry Andric         Features.push_back("+lasx");
212*0fca6ea1SDimitry Andric       }
213*0fca6ea1SDimitry Andric     } else if (MSIMD == "none") {
214*0fca6ea1SDimitry Andric       if (llvm::find(Features, "+lsx") != Features.end())
215*0fca6ea1SDimitry Andric         Features.push_back("-lsx");
216*0fca6ea1SDimitry Andric       if (llvm::find(Features, "+lasx") != Features.end())
217*0fca6ea1SDimitry Andric         Features.push_back("-lasx");
218*0fca6ea1SDimitry Andric     } else {
219*0fca6ea1SDimitry Andric       D.Diag(diag::err_drv_loongarch_invalid_msimd_EQ) << MSIMD;
220*0fca6ea1SDimitry Andric     }
221*0fca6ea1SDimitry Andric   }
2225f757f3fSDimitry Andric 
2235f757f3fSDimitry Andric   // Select lsx feature determined by -m[no-]lsx.
2245f757f3fSDimitry Andric   if (const Arg *A = Args.getLastArg(options::OPT_mlsx, options::OPT_mno_lsx)) {
2255f757f3fSDimitry Andric     // LSX depends on 64-bit FPU.
2265f757f3fSDimitry Andric     // -m*-float and -mfpu=none/0/32 conflict with -mlsx.
2275f757f3fSDimitry Andric     if (A->getOption().matches(options::OPT_mlsx)) {
2285f757f3fSDimitry Andric       if (llvm::find(Features, "-d") != Features.end())
229*0fca6ea1SDimitry Andric         D.Diag(diag::err_drv_loongarch_wrong_fpu_width) << /*LSX*/ 0;
2305f757f3fSDimitry Andric       else /*-mlsx*/
2315f757f3fSDimitry Andric         Features.push_back("+lsx");
2325f757f3fSDimitry Andric     } else /*-mno-lsx*/ {
2335f757f3fSDimitry Andric       Features.push_back("-lsx");
2345f757f3fSDimitry Andric     }
2355f757f3fSDimitry Andric   }
2365f757f3fSDimitry Andric 
2375f757f3fSDimitry Andric   // Select lasx feature determined by -m[no-]lasx.
2385f757f3fSDimitry Andric   if (const Arg *A =
2395f757f3fSDimitry Andric           Args.getLastArg(options::OPT_mlasx, options::OPT_mno_lasx)) {
2405f757f3fSDimitry Andric     // LASX depends on 64-bit FPU and LSX.
2415f757f3fSDimitry Andric     // -mno-lsx conflicts with -mlasx.
2425f757f3fSDimitry Andric     if (A->getOption().matches(options::OPT_mlasx)) {
2435f757f3fSDimitry Andric       if (llvm::find(Features, "-d") != Features.end())
244*0fca6ea1SDimitry Andric         D.Diag(diag::err_drv_loongarch_wrong_fpu_width) << /*LASX*/ 1;
2455f757f3fSDimitry Andric       else { /*-mlasx*/
2465f757f3fSDimitry Andric         Features.push_back("+lsx");
2475f757f3fSDimitry Andric         Features.push_back("+lasx");
2485f757f3fSDimitry Andric       }
2495f757f3fSDimitry Andric     } else /*-mno-lasx*/
2505f757f3fSDimitry Andric       Features.push_back("-lasx");
2515f757f3fSDimitry Andric   }
252bdd1243dSDimitry Andric }
2538a4dda33SDimitry Andric 
2548a4dda33SDimitry Andric std::string loongarch::postProcessTargetCPUString(const std::string &CPU,
2558a4dda33SDimitry Andric                                                   const llvm::Triple &Triple) {
2568a4dda33SDimitry Andric   std::string CPUString = CPU;
2578a4dda33SDimitry Andric   if (CPUString == "native") {
2588a4dda33SDimitry Andric     CPUString = llvm::sys::getHostCPUName();
2598a4dda33SDimitry Andric     if (CPUString == "generic")
2608a4dda33SDimitry Andric       CPUString = llvm::LoongArch::getDefaultArch(Triple.isLoongArch64());
2618a4dda33SDimitry Andric   }
2628a4dda33SDimitry Andric   if (CPUString.empty())
2638a4dda33SDimitry Andric     CPUString = llvm::LoongArch::getDefaultArch(Triple.isLoongArch64());
2648a4dda33SDimitry Andric   return CPUString;
2658a4dda33SDimitry Andric }
2668a4dda33SDimitry Andric 
2678a4dda33SDimitry Andric std::string loongarch::getLoongArchTargetCPU(const llvm::opt::ArgList &Args,
2688a4dda33SDimitry Andric                                              const llvm::Triple &Triple) {
2698a4dda33SDimitry Andric   std::string CPU;
270*0fca6ea1SDimitry Andric   std::string Arch;
2718a4dda33SDimitry Andric   // If we have -march, use that.
272*0fca6ea1SDimitry Andric   if (const Arg *A = Args.getLastArg(options::OPT_march_EQ)) {
273*0fca6ea1SDimitry Andric     Arch = A->getValue();
274*0fca6ea1SDimitry Andric     if (Arch == "la64v1.0" || Arch == "la64v1.1")
275*0fca6ea1SDimitry Andric       CPU = llvm::LoongArch::getDefaultArch(Triple.isLoongArch64());
276*0fca6ea1SDimitry Andric     else
277*0fca6ea1SDimitry Andric       CPU = Arch;
278*0fca6ea1SDimitry Andric   }
2798a4dda33SDimitry Andric   return postProcessTargetCPUString(CPU, Triple);
2808a4dda33SDimitry Andric }
281