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