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 
getLoongArchABI(const Driver & D,const ArgList & Args,const llvm::Triple & Triple)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 
getLoongArchTargetFeatures(const Driver & D,const llvm::Triple & Triple,const ArgList & Args,std::vector<StringRef> & Features)126 void loongarch::getLoongArchTargetFeatures(const Driver &D,
127                                            const llvm::Triple &Triple,
128                                            const ArgList &Args,
129                                            std::vector<StringRef> &Features) {
130   std::string ArchName;
131   if (const Arg *A = Args.getLastArg(options::OPT_march_EQ))
132     ArchName = A->getValue();
133   ArchName = postProcessTargetCPUString(ArchName, Triple);
134   llvm::LoongArch::getArchFeatures(ArchName, Features);
135 
136   // Select floating-point features determined by -mdouble-float,
137   // -msingle-float, -msoft-float and -mfpu.
138   // Note: -m*-float wins any other options.
139   if (const Arg *A = Args.getLastArg(options::OPT_mdouble_float,
140                                      options::OPT_msingle_float,
141                                      options::OPT_msoft_float)) {
142     if (A->getOption().matches(options::OPT_mdouble_float)) {
143       Features.push_back("+f");
144       Features.push_back("+d");
145     } else if (A->getOption().matches(options::OPT_msingle_float)) {
146       Features.push_back("+f");
147       Features.push_back("-d");
148     } else /*Soft-float*/ {
149       Features.push_back("-f");
150       Features.push_back("-d");
151     }
152   } else if (const Arg *A = Args.getLastArg(options::OPT_mfpu_EQ)) {
153     StringRef FPU = A->getValue();
154     if (FPU == "64") {
155       Features.push_back("+f");
156       Features.push_back("+d");
157     } else if (FPU == "32") {
158       Features.push_back("+f");
159       Features.push_back("-d");
160     } else if (FPU == "0" || FPU == "none") {
161       Features.push_back("-f");
162       Features.push_back("-d");
163     } else {
164       D.Diag(diag::err_drv_loongarch_invalid_mfpu_EQ) << FPU;
165     }
166   }
167 
168   // Select the `ual` feature determined by -m[no-]unaligned-access
169   // or the alias -m[no-]strict-align.
170   AddTargetFeature(Args, Features, options::OPT_munaligned_access,
171                    options::OPT_mno_unaligned_access, "ual");
172 
173   // Accept but warn about these TargetSpecific options.
174   if (Arg *A = Args.getLastArgNoClaim(options::OPT_mabi_EQ))
175     A->ignoreTargetSpecific();
176   if (Arg *A = Args.getLastArgNoClaim(options::OPT_mfpu_EQ))
177     A->ignoreTargetSpecific();
178 
179   // Select lsx feature determined by -m[no-]lsx.
180   if (const Arg *A = Args.getLastArg(options::OPT_mlsx, options::OPT_mno_lsx)) {
181     // LSX depends on 64-bit FPU.
182     // -m*-float and -mfpu=none/0/32 conflict with -mlsx.
183     if (A->getOption().matches(options::OPT_mlsx)) {
184       if (llvm::find(Features, "-d") != Features.end())
185         D.Diag(diag::err_drv_loongarch_wrong_fpu_width_for_lsx);
186       else /*-mlsx*/
187         Features.push_back("+lsx");
188     } else /*-mno-lsx*/ {
189       Features.push_back("-lsx");
190     }
191   }
192 
193   // Select lasx feature determined by -m[no-]lasx.
194   if (const Arg *A =
195           Args.getLastArg(options::OPT_mlasx, options::OPT_mno_lasx)) {
196     // LASX depends on 64-bit FPU and LSX.
197     // -mno-lsx conflicts with -mlasx.
198     if (A->getOption().matches(options::OPT_mlasx)) {
199       if (llvm::find(Features, "-d") != Features.end())
200         D.Diag(diag::err_drv_loongarch_wrong_fpu_width_for_lasx);
201       else if (llvm::find(Features, "-lsx") != Features.end())
202         D.Diag(diag::err_drv_loongarch_invalid_simd_option_combination);
203       else { /*-mlasx*/
204         Features.push_back("+lsx");
205         Features.push_back("+lasx");
206       }
207     } else /*-mno-lasx*/
208       Features.push_back("-lasx");
209   }
210 }
211 
postProcessTargetCPUString(const std::string & CPU,const llvm::Triple & Triple)212 std::string loongarch::postProcessTargetCPUString(const std::string &CPU,
213                                                   const llvm::Triple &Triple) {
214   std::string CPUString = CPU;
215   if (CPUString == "native") {
216     CPUString = llvm::sys::getHostCPUName();
217     if (CPUString == "generic")
218       CPUString = llvm::LoongArch::getDefaultArch(Triple.isLoongArch64());
219   }
220   if (CPUString.empty())
221     CPUString = llvm::LoongArch::getDefaultArch(Triple.isLoongArch64());
222   return CPUString;
223 }
224 
getLoongArchTargetCPU(const llvm::opt::ArgList & Args,const llvm::Triple & Triple)225 std::string loongarch::getLoongArchTargetCPU(const llvm::opt::ArgList &Args,
226                                              const llvm::Triple &Triple) {
227   std::string CPU;
228   // If we have -march, use that.
229   if (const Arg *A = Args.getLastArg(options::OPT_march_EQ))
230     CPU = A->getValue();
231   return postProcessTargetCPUString(CPU, Triple);
232 }
233