1//===--- LoongArch.cpp - Implement LoongArch target feature support -------===//
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// This file implements LoongArch TargetInfo objects.
10//
11//===----------------------------------------------------------------------===//
12
13#include "LoongArch.h"
14#include "clang/Basic/Diagnostic.h"
15#include "clang/Basic/MacroBuilder.h"
16#include "clang/Basic/TargetBuiltins.h"
17#include "llvm/Support/raw_ostream.h"
18#include "llvm/TargetParser/LoongArchTargetParser.h"
19
20using namespace clang;
21using namespace clang::targets;
22
23ArrayRef<const char *> LoongArchTargetInfo::getGCCRegNames() const {
24 static const char *const GCCRegNames[] = {
25 // General purpose registers.
26 "$r0", "$r1", "$r2", "$r3", "$r4", "$r5", "$r6", "$r7", "$r8", "$r9",
27 "$r10", "$r11", "$r12", "$r13", "$r14", "$r15", "$r16", "$r17", "$r18",
28 "$r19", "$r20", "$r21", "$r22", "$r23", "$r24", "$r25", "$r26", "$r27",
29 "$r28", "$r29", "$r30", "$r31",
30 // Floating point registers.
31 "$f0", "$f1", "$f2", "$f3", "$f4", "$f5", "$f6", "$f7", "$f8", "$f9",
32 "$f10", "$f11", "$f12", "$f13", "$f14", "$f15", "$f16", "$f17", "$f18",
33 "$f19", "$f20", "$f21", "$f22", "$f23", "$f24", "$f25", "$f26", "$f27",
34 "$f28", "$f29", "$f30", "$f31",
35 // Condition flag registers.
36 "$fcc0", "$fcc1", "$fcc2", "$fcc3", "$fcc4", "$fcc5", "$fcc6", "$fcc7",
37 // 128-bit vector registers.
38 "$vr0", "$vr1", "$vr2", "$vr3", "$vr4", "$vr5", "$vr6", "$vr7", "$vr8",
39 "$vr9", "$vr10", "$vr11", "$vr12", "$vr13", "$vr14", "$vr15", "$vr16",
40 "$vr17", "$vr18", "$vr19", "$vr20", "$vr21", "$vr22", "$vr23", "$vr24",
41 "$vr25", "$vr26", "$vr27", "$vr28", "$vr29", "$vr30", "$vr31",
42 // 256-bit vector registers.
43 "$xr0", "$xr1", "$xr2", "$xr3", "$xr4", "$xr5", "$xr6", "$xr7", "$xr8",
44 "$xr9", "$xr10", "$xr11", "$xr12", "$xr13", "$xr14", "$xr15", "$xr16",
45 "$xr17", "$xr18", "$xr19", "$xr20", "$xr21", "$xr22", "$xr23", "$xr24",
46 "$xr25", "$xr26", "$xr27", "$xr28", "$xr29", "$xr30", "$xr31"};
47 return llvm::ArrayRef(GCCRegNames);
48}
49
50ArrayRef<TargetInfo::GCCRegAlias>
51LoongArchTargetInfo::getGCCRegAliases() const {
52 static const TargetInfo::GCCRegAlias GCCRegAliases[] = {
53 {.Aliases: {"zero", "$zero", "r0"}, .Register: "$r0"},
54 {.Aliases: {"ra", "$ra", "r1"}, .Register: "$r1"},
55 {.Aliases: {"tp", "$tp", "r2"}, .Register: "$r2"},
56 {.Aliases: {"sp", "$sp", "r3"}, .Register: "$r3"},
57 {.Aliases: {"a0", "$a0", "r4"}, .Register: "$r4"},
58 {.Aliases: {"a1", "$a1", "r5"}, .Register: "$r5"},
59 {.Aliases: {"a2", "$a2", "r6"}, .Register: "$r6"},
60 {.Aliases: {"a3", "$a3", "r7"}, .Register: "$r7"},
61 {.Aliases: {"a4", "$a4", "r8"}, .Register: "$r8"},
62 {.Aliases: {"a5", "$a5", "r9"}, .Register: "$r9"},
63 {.Aliases: {"a6", "$a6", "r10"}, .Register: "$r10"},
64 {.Aliases: {"a7", "$a7", "r11"}, .Register: "$r11"},
65 {.Aliases: {"t0", "$t0", "r12"}, .Register: "$r12"},
66 {.Aliases: {"t1", "$t1", "r13"}, .Register: "$r13"},
67 {.Aliases: {"t2", "$t2", "r14"}, .Register: "$r14"},
68 {.Aliases: {"t3", "$t3", "r15"}, .Register: "$r15"},
69 {.Aliases: {"t4", "$t4", "r16"}, .Register: "$r16"},
70 {.Aliases: {"t5", "$t5", "r17"}, .Register: "$r17"},
71 {.Aliases: {"t6", "$t6", "r18"}, .Register: "$r18"},
72 {.Aliases: {"t7", "$t7", "r19"}, .Register: "$r19"},
73 {.Aliases: {"t8", "$t8", "r20"}, .Register: "$r20"},
74 {.Aliases: {"r21"}, .Register: "$r21"},
75 {.Aliases: {"s9", "$s9", "r22", "fp", "$fp"}, .Register: "$r22"},
76 {.Aliases: {"s0", "$s0", "r23"}, .Register: "$r23"},
77 {.Aliases: {"s1", "$s1", "r24"}, .Register: "$r24"},
78 {.Aliases: {"s2", "$s2", "r25"}, .Register: "$r25"},
79 {.Aliases: {"s3", "$s3", "r26"}, .Register: "$r26"},
80 {.Aliases: {"s4", "$s4", "r27"}, .Register: "$r27"},
81 {.Aliases: {"s5", "$s5", "r28"}, .Register: "$r28"},
82 {.Aliases: {"s6", "$s6", "r29"}, .Register: "$r29"},
83 {.Aliases: {"s7", "$s7", "r30"}, .Register: "$r30"},
84 {.Aliases: {"s8", "$s8", "r31"}, .Register: "$r31"},
85 {.Aliases: {"$fa0"}, .Register: "$f0"},
86 {.Aliases: {"$fa1"}, .Register: "$f1"},
87 {.Aliases: {"$fa2"}, .Register: "$f2"},
88 {.Aliases: {"$fa3"}, .Register: "$f3"},
89 {.Aliases: {"$fa4"}, .Register: "$f4"},
90 {.Aliases: {"$fa5"}, .Register: "$f5"},
91 {.Aliases: {"$fa6"}, .Register: "$f6"},
92 {.Aliases: {"$fa7"}, .Register: "$f7"},
93 {.Aliases: {"$ft0"}, .Register: "$f8"},
94 {.Aliases: {"$ft1"}, .Register: "$f9"},
95 {.Aliases: {"$ft2"}, .Register: "$f10"},
96 {.Aliases: {"$ft3"}, .Register: "$f11"},
97 {.Aliases: {"$ft4"}, .Register: "$f12"},
98 {.Aliases: {"$ft5"}, .Register: "$f13"},
99 {.Aliases: {"$ft6"}, .Register: "$f14"},
100 {.Aliases: {"$ft7"}, .Register: "$f15"},
101 {.Aliases: {"$ft8"}, .Register: "$f16"},
102 {.Aliases: {"$ft9"}, .Register: "$f17"},
103 {.Aliases: {"$ft10"}, .Register: "$f18"},
104 {.Aliases: {"$ft11"}, .Register: "$f19"},
105 {.Aliases: {"$ft12"}, .Register: "$f20"},
106 {.Aliases: {"$ft13"}, .Register: "$f21"},
107 {.Aliases: {"$ft14"}, .Register: "$f22"},
108 {.Aliases: {"$ft15"}, .Register: "$f23"},
109 {.Aliases: {"$fs0"}, .Register: "$f24"},
110 {.Aliases: {"$fs1"}, .Register: "$f25"},
111 {.Aliases: {"$fs2"}, .Register: "$f26"},
112 {.Aliases: {"$fs3"}, .Register: "$f27"},
113 {.Aliases: {"$fs4"}, .Register: "$f28"},
114 {.Aliases: {"$fs5"}, .Register: "$f29"},
115 {.Aliases: {"$fs6"}, .Register: "$f30"},
116 {.Aliases: {"$fs7"}, .Register: "$f31"},
117 };
118 return llvm::ArrayRef(GCCRegAliases);
119}
120
121bool LoongArchTargetInfo::validateAsmConstraint(
122 const char *&Name, TargetInfo::ConstraintInfo &Info) const {
123 // See the GCC definitions here:
124 // https://gcc.gnu.org/onlinedocs/gccint/Machine-Constraints.html
125 // Note that the 'm' constraint is handled in TargetInfo.
126 switch (*Name) {
127 default:
128 return false;
129 case 'f':
130 // A floating-point register (if available).
131 Info.setAllowsRegister();
132 return true;
133 case 'k':
134 // A memory operand whose address is formed by a base register and
135 // (optionally scaled) index register.
136 Info.setAllowsMemory();
137 return true;
138 case 'l':
139 // A signed 16-bit constant.
140 Info.setRequiresImmediate(Min: -32768, Max: 32767);
141 return true;
142 case 'I':
143 // A signed 12-bit constant (for arithmetic instructions).
144 Info.setRequiresImmediate(Min: -2048, Max: 2047);
145 return true;
146 case 'J':
147 // Integer zero.
148 Info.setRequiresImmediate(0);
149 return true;
150 case 'K':
151 // An unsigned 12-bit constant (for logic instructions).
152 Info.setRequiresImmediate(Min: 0, Max: 4095);
153 return true;
154 case 'Z':
155 // ZB: An address that is held in a general-purpose register. The offset is
156 // zero.
157 // ZC: A memory operand whose address is formed by a base register
158 // and offset that is suitable for use in instructions with the same
159 // addressing mode as ll.w and sc.w.
160 if (Name[1] == 'C' || Name[1] == 'B') {
161 Info.setAllowsMemory();
162 ++Name; // Skip over 'Z'.
163 return true;
164 }
165 return false;
166 }
167}
168
169std::string
170LoongArchTargetInfo::convertConstraint(const char *&Constraint) const {
171 std::string R;
172 switch (*Constraint) {
173 case 'Z':
174 // "ZC"/"ZB" are two-character constraints; add "^" hint for later
175 // parsing.
176 R = "^" + std::string(Constraint, 2);
177 ++Constraint;
178 break;
179 default:
180 R = TargetInfo::convertConstraint(Constraint);
181 break;
182 }
183 return R;
184}
185
186void LoongArchTargetInfo::getTargetDefines(const LangOptions &Opts,
187 MacroBuilder &Builder) const {
188 Builder.defineMacro(Name: "__loongarch__");
189 unsigned GRLen = getRegisterWidth();
190 Builder.defineMacro(Name: "__loongarch_grlen", Value: Twine(GRLen));
191 if (GRLen == 64)
192 Builder.defineMacro(Name: "__loongarch64");
193
194 if (HasFeatureD)
195 Builder.defineMacro(Name: "__loongarch_frlen", Value: "64");
196 else if (HasFeatureF)
197 Builder.defineMacro(Name: "__loongarch_frlen", Value: "32");
198 else
199 Builder.defineMacro(Name: "__loongarch_frlen", Value: "0");
200
201 // Define __loongarch_arch.
202 StringRef ArchName = getCPU();
203 if (ArchName == "loongarch64") {
204 if (HasFeatureLSX) {
205 // TODO: As more features of the V1.1 ISA are supported, a unified "v1.1"
206 // arch feature set will be used to include all sub-features belonging to
207 // the V1.1 ISA version.
208 if (HasFeatureFrecipe)
209 Builder.defineMacro(Name: "__loongarch_arch",
210 Value: Twine('"') + "la64v1.1" + Twine('"'));
211 else
212 Builder.defineMacro(Name: "__loongarch_arch",
213 Value: Twine('"') + "la64v1.0" + Twine('"'));
214 } else {
215 Builder.defineMacro(Name: "__loongarch_arch",
216 Value: Twine('"') + ArchName + Twine('"'));
217 }
218 } else {
219 Builder.defineMacro(Name: "__loongarch_arch", Value: Twine('"') + ArchName + Twine('"'));
220 }
221
222 // Define __loongarch_tune.
223 StringRef TuneCPU = getTargetOpts().TuneCPU;
224 if (TuneCPU.empty())
225 TuneCPU = ArchName;
226 Builder.defineMacro(Name: "__loongarch_tune", Value: Twine('"') + TuneCPU + Twine('"'));
227
228 if (HasFeatureLASX) {
229 Builder.defineMacro(Name: "__loongarch_simd_width", Value: "256");
230 Builder.defineMacro(Name: "__loongarch_sx", Value: Twine(1));
231 Builder.defineMacro(Name: "__loongarch_asx", Value: Twine(1));
232 } else if (HasFeatureLSX) {
233 Builder.defineMacro(Name: "__loongarch_simd_width", Value: "128");
234 Builder.defineMacro(Name: "__loongarch_sx", Value: Twine(1));
235 }
236 if (HasFeatureFrecipe)
237 Builder.defineMacro(Name: "__loongarch_frecipe", Value: Twine(1));
238
239 StringRef ABI = getABI();
240 if (ABI == "lp64d" || ABI == "lp64f" || ABI == "lp64s")
241 Builder.defineMacro(Name: "__loongarch_lp64");
242
243 if (ABI == "lp64d" || ABI == "ilp32d") {
244 Builder.defineMacro(Name: "__loongarch_hard_float");
245 Builder.defineMacro(Name: "__loongarch_double_float");
246 } else if (ABI == "lp64f" || ABI == "ilp32f") {
247 Builder.defineMacro(Name: "__loongarch_hard_float");
248 Builder.defineMacro(Name: "__loongarch_single_float");
249 } else if (ABI == "lp64s" || ABI == "ilp32s") {
250 Builder.defineMacro(Name: "__loongarch_soft_float");
251 }
252
253 Builder.defineMacro(Name: "__GCC_HAVE_SYNC_COMPARE_AND_SWAP_1");
254 Builder.defineMacro(Name: "__GCC_HAVE_SYNC_COMPARE_AND_SWAP_2");
255 Builder.defineMacro(Name: "__GCC_HAVE_SYNC_COMPARE_AND_SWAP_4");
256 if (GRLen == 64)
257 Builder.defineMacro(Name: "__GCC_HAVE_SYNC_COMPARE_AND_SWAP_8");
258}
259
260static constexpr Builtin::Info BuiltinInfo[] = {
261#define BUILTIN(ID, TYPE, ATTRS) \
262 {#ID, TYPE, ATTRS, nullptr, HeaderDesc::NO_HEADER, ALL_LANGUAGES},
263#define TARGET_BUILTIN(ID, TYPE, ATTRS, FEATURE) \
264 {#ID, TYPE, ATTRS, FEATURE, HeaderDesc::NO_HEADER, ALL_LANGUAGES},
265#include "clang/Basic/BuiltinsLoongArch.def"
266};
267
268bool LoongArchTargetInfo::initFeatureMap(
269 llvm::StringMap<bool> &Features, DiagnosticsEngine &Diags, StringRef CPU,
270 const std::vector<std::string> &FeaturesVec) const {
271 if (getTriple().getArch() == llvm::Triple::loongarch64)
272 Features["64bit"] = true;
273 if (getTriple().getArch() == llvm::Triple::loongarch32)
274 Features["32bit"] = true;
275
276 return TargetInfo::initFeatureMap(Features, Diags, CPU, FeatureVec: FeaturesVec);
277}
278
279/// Return true if has this feature.
280bool LoongArchTargetInfo::hasFeature(StringRef Feature) const {
281 bool Is64Bit = getTriple().getArch() == llvm::Triple::loongarch64;
282 // TODO: Handle more features.
283 return llvm::StringSwitch<bool>(Feature)
284 .Case(S: "loongarch32", Value: !Is64Bit)
285 .Case(S: "loongarch64", Value: Is64Bit)
286 .Case(S: "32bit", Value: !Is64Bit)
287 .Case(S: "64bit", Value: Is64Bit)
288 .Case(S: "lsx", Value: HasFeatureLSX)
289 .Case(S: "lasx", Value: HasFeatureLASX)
290 .Default(Value: false);
291}
292
293ArrayRef<Builtin::Info> LoongArchTargetInfo::getTargetBuiltins() const {
294 return llvm::ArrayRef(BuiltinInfo, clang::LoongArch::LastTSBuiltin -
295 Builtin::FirstTSBuiltin);
296}
297
298bool LoongArchTargetInfo::handleTargetFeatures(
299 std::vector<std::string> &Features, DiagnosticsEngine &Diags) {
300 for (const auto &Feature : Features) {
301 if (Feature == "+d" || Feature == "+f") {
302 // "d" implies "f".
303 HasFeatureF = true;
304 if (Feature == "+d") {
305 HasFeatureD = true;
306 }
307 } else if (Feature == "+lsx")
308 HasFeatureLSX = true;
309 else if (Feature == "+lasx")
310 HasFeatureLASX = true;
311 else if (Feature == "-ual")
312 HasUnalignedAccess = false;
313 else if (Feature == "+frecipe")
314 HasFeatureFrecipe = true;
315 }
316 return true;
317}
318
319bool LoongArchTargetInfo::isValidCPUName(StringRef Name) const {
320 return llvm::LoongArch::isValidCPUName(TuneCPU: Name);
321}
322
323void LoongArchTargetInfo::fillValidCPUList(
324 SmallVectorImpl<StringRef> &Values) const {
325 llvm::LoongArch::fillValidCPUList(Values);
326}
327