xref: /freebsd/contrib/llvm-project/clang/lib/Basic/Targets/LoongArch.cpp (revision e64bea71c21eb42e97aa615188ba91f6cce0d36d)
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/TargetParser/LoongArchTargetParser.h"
18 
19 using namespace clang;
20 using namespace clang::targets;
21 
getGCCRegNames() const22 ArrayRef<const char *> LoongArchTargetInfo::getGCCRegNames() const {
23   static const char *const GCCRegNames[] = {
24       // General purpose registers.
25       "$r0", "$r1", "$r2", "$r3", "$r4", "$r5", "$r6", "$r7", "$r8", "$r9",
26       "$r10", "$r11", "$r12", "$r13", "$r14", "$r15", "$r16", "$r17", "$r18",
27       "$r19", "$r20", "$r21", "$r22", "$r23", "$r24", "$r25", "$r26", "$r27",
28       "$r28", "$r29", "$r30", "$r31",
29       // Floating point registers.
30       "$f0", "$f1", "$f2", "$f3", "$f4", "$f5", "$f6", "$f7", "$f8", "$f9",
31       "$f10", "$f11", "$f12", "$f13", "$f14", "$f15", "$f16", "$f17", "$f18",
32       "$f19", "$f20", "$f21", "$f22", "$f23", "$f24", "$f25", "$f26", "$f27",
33       "$f28", "$f29", "$f30", "$f31",
34       // Condition flag registers.
35       "$fcc0", "$fcc1", "$fcc2", "$fcc3", "$fcc4", "$fcc5", "$fcc6", "$fcc7",
36       // 128-bit vector registers.
37       "$vr0", "$vr1", "$vr2", "$vr3", "$vr4", "$vr5", "$vr6", "$vr7", "$vr8",
38       "$vr9", "$vr10", "$vr11", "$vr12", "$vr13", "$vr14", "$vr15", "$vr16",
39       "$vr17", "$vr18", "$vr19", "$vr20", "$vr21", "$vr22", "$vr23", "$vr24",
40       "$vr25", "$vr26", "$vr27", "$vr28", "$vr29", "$vr30", "$vr31",
41       // 256-bit vector registers.
42       "$xr0", "$xr1", "$xr2", "$xr3", "$xr4", "$xr5", "$xr6", "$xr7", "$xr8",
43       "$xr9", "$xr10", "$xr11", "$xr12", "$xr13", "$xr14", "$xr15", "$xr16",
44       "$xr17", "$xr18", "$xr19", "$xr20", "$xr21", "$xr22", "$xr23", "$xr24",
45       "$xr25", "$xr26", "$xr27", "$xr28", "$xr29", "$xr30", "$xr31"};
46   return llvm::ArrayRef(GCCRegNames);
47 }
48 
49 ArrayRef<TargetInfo::GCCRegAlias>
getGCCRegAliases() const50 LoongArchTargetInfo::getGCCRegAliases() const {
51   static const TargetInfo::GCCRegAlias GCCRegAliases[] = {
52       {{"zero", "$zero", "r0"}, "$r0"},
53       {{"ra", "$ra", "r1"}, "$r1"},
54       {{"tp", "$tp", "r2"}, "$r2"},
55       {{"sp", "$sp", "r3"}, "$r3"},
56       {{"a0", "$a0", "r4"}, "$r4"},
57       {{"a1", "$a1", "r5"}, "$r5"},
58       {{"a2", "$a2", "r6"}, "$r6"},
59       {{"a3", "$a3", "r7"}, "$r7"},
60       {{"a4", "$a4", "r8"}, "$r8"},
61       {{"a5", "$a5", "r9"}, "$r9"},
62       {{"a6", "$a6", "r10"}, "$r10"},
63       {{"a7", "$a7", "r11"}, "$r11"},
64       {{"t0", "$t0", "r12"}, "$r12"},
65       {{"t1", "$t1", "r13"}, "$r13"},
66       {{"t2", "$t2", "r14"}, "$r14"},
67       {{"t3", "$t3", "r15"}, "$r15"},
68       {{"t4", "$t4", "r16"}, "$r16"},
69       {{"t5", "$t5", "r17"}, "$r17"},
70       {{"t6", "$t6", "r18"}, "$r18"},
71       {{"t7", "$t7", "r19"}, "$r19"},
72       {{"t8", "$t8", "r20"}, "$r20"},
73       {{"r21"}, "$r21"},
74       {{"s9", "$s9", "r22", "fp", "$fp"}, "$r22"},
75       {{"s0", "$s0", "r23"}, "$r23"},
76       {{"s1", "$s1", "r24"}, "$r24"},
77       {{"s2", "$s2", "r25"}, "$r25"},
78       {{"s3", "$s3", "r26"}, "$r26"},
79       {{"s4", "$s4", "r27"}, "$r27"},
80       {{"s5", "$s5", "r28"}, "$r28"},
81       {{"s6", "$s6", "r29"}, "$r29"},
82       {{"s7", "$s7", "r30"}, "$r30"},
83       {{"s8", "$s8", "r31"}, "$r31"},
84       {{"fa0", "$fa0", "f0"}, "$f0"},
85       {{"fa1", "$fa1", "f1"}, "$f1"},
86       {{"fa2", "$fa2", "f2"}, "$f2"},
87       {{"fa3", "$fa3", "f3"}, "$f3"},
88       {{"fa4", "$fa4", "f4"}, "$f4"},
89       {{"fa5", "$fa5", "f5"}, "$f5"},
90       {{"fa6", "$fa6", "f6"}, "$f6"},
91       {{"fa7", "$fa7", "f7"}, "$f7"},
92       {{"ft0", "$ft0", "f8"}, "$f8"},
93       {{"ft1", "$ft1", "f9"}, "$f9"},
94       {{"ft2", "$ft2", "f10"}, "$f10"},
95       {{"ft3", "$ft3", "f11"}, "$f11"},
96       {{"ft4", "$ft4", "f12"}, "$f12"},
97       {{"ft5", "$ft5", "f13"}, "$f13"},
98       {{"ft6", "$ft6", "f14"}, "$f14"},
99       {{"ft7", "$ft7", "f15"}, "$f15"},
100       {{"ft8", "$ft8", "f16"}, "$f16"},
101       {{"ft9", "$ft9", "f17"}, "$f17"},
102       {{"ft10", "$ft10", "f18"}, "$f18"},
103       {{"ft11", "$ft11", "f19"}, "$f19"},
104       {{"ft12", "$ft12", "f20"}, "$f20"},
105       {{"ft13", "$ft13", "f21"}, "$f21"},
106       {{"ft14", "$ft14", "f22"}, "$f22"},
107       {{"ft15", "$ft15", "f23"}, "$f23"},
108       {{"fs0", "$fs0", "f24"}, "$f24"},
109       {{"fs1", "$fs1", "f25"}, "$f25"},
110       {{"fs2", "$fs2", "f26"}, "$f26"},
111       {{"fs3", "$fs3", "f27"}, "$f27"},
112       {{"fs4", "$fs4", "f28"}, "$f28"},
113       {{"fs5", "$fs5", "f29"}, "$f29"},
114       {{"fs6", "$fs6", "f30"}, "$f30"},
115       {{"fs7", "$fs7", "f31"}, "$f31"},
116       {{"fcc0"}, "$fcc0"},
117       {{"fcc1"}, "$fcc1"},
118       {{"fcc2"}, "$fcc2"},
119       {{"fcc3"}, "$fcc3"},
120       {{"fcc4"}, "$fcc4"},
121       {{"fcc5"}, "$fcc5"},
122       {{"fcc6"}, "$fcc6"},
123       {{"fcc7"}, "$fcc7"},
124   };
125   return llvm::ArrayRef(GCCRegAliases);
126 }
127 
validateAsmConstraint(const char * & Name,TargetInfo::ConstraintInfo & Info) const128 bool LoongArchTargetInfo::validateAsmConstraint(
129     const char *&Name, TargetInfo::ConstraintInfo &Info) const {
130   // See the GCC definitions here:
131   // https://gcc.gnu.org/onlinedocs/gccint/Machine-Constraints.html
132   // Note that the 'm' constraint is handled in TargetInfo.
133   switch (*Name) {
134   default:
135     return false;
136   case 'f':
137     // A floating-point register (if available).
138     Info.setAllowsRegister();
139     return true;
140   case 'k':
141     // A memory operand whose address is formed by a base register and
142     // (optionally scaled) index register.
143     Info.setAllowsMemory();
144     return true;
145   case 'l':
146     // A signed 16-bit constant.
147     Info.setRequiresImmediate(-32768, 32767);
148     return true;
149   case 'q':
150     // A general-purpose register except for $r0 and $r1 (for the csrxchg
151     // instruction)
152     Info.setAllowsRegister();
153     return true;
154   case 'I':
155     // A signed 12-bit constant (for arithmetic instructions).
156     Info.setRequiresImmediate(-2048, 2047);
157     return true;
158   case 'J':
159     // Integer zero.
160     Info.setRequiresImmediate(0);
161     return true;
162   case 'K':
163     // An unsigned 12-bit constant (for logic instructions).
164     Info.setRequiresImmediate(0, 4095);
165     return true;
166   case 'Z':
167     // ZB: An address that is held in a general-purpose register. The offset is
168     //     zero.
169     // ZC: A memory operand whose address is formed by a base register
170     //     and offset that is suitable for use in instructions with the same
171     //     addressing mode as ll.w and sc.w.
172     if (Name[1] == 'C' || Name[1] == 'B') {
173       Info.setAllowsMemory();
174       ++Name; // Skip over 'Z'.
175       return true;
176     }
177     return false;
178   }
179 }
180 
181 std::string
convertConstraint(const char * & Constraint) const182 LoongArchTargetInfo::convertConstraint(const char *&Constraint) const {
183   std::string R;
184   switch (*Constraint) {
185   case 'Z':
186     // "ZC"/"ZB" are two-character constraints; add "^" hint for later
187     // parsing.
188     R = "^" + std::string(Constraint, 2);
189     ++Constraint;
190     break;
191   default:
192     R = TargetInfo::convertConstraint(Constraint);
193     break;
194   }
195   return R;
196 }
197 
getTargetDefines(const LangOptions & Opts,MacroBuilder & Builder) const198 void LoongArchTargetInfo::getTargetDefines(const LangOptions &Opts,
199                                            MacroBuilder &Builder) const {
200   Builder.defineMacro("__loongarch__");
201   unsigned GRLen = getRegisterWidth();
202   Builder.defineMacro("__loongarch_grlen", Twine(GRLen));
203   if (GRLen == 64)
204     Builder.defineMacro("__loongarch64");
205 
206   if (HasFeatureD)
207     Builder.defineMacro("__loongarch_frlen", "64");
208   else if (HasFeatureF)
209     Builder.defineMacro("__loongarch_frlen", "32");
210   else
211     Builder.defineMacro("__loongarch_frlen", "0");
212 
213   // Define __loongarch_arch.
214   StringRef ArchName = getCPU();
215   if (ArchName == "loongarch64") {
216     if (HasFeatureLSX) {
217       // TODO: As more features of the V1.1 ISA are supported, a unified "v1.1"
218       // arch feature set will be used to include all sub-features belonging to
219       // the V1.1 ISA version.
220       if (HasFeatureFrecipe && HasFeatureLAM_BH && HasFeatureLAMCAS &&
221           HasFeatureLD_SEQ_SA && HasFeatureDiv32 && HasFeatureSCQ)
222         Builder.defineMacro("__loongarch_arch",
223                             Twine('"') + "la64v1.1" + Twine('"'));
224       else
225         Builder.defineMacro("__loongarch_arch",
226                             Twine('"') + "la64v1.0" + Twine('"'));
227     } else {
228       Builder.defineMacro("__loongarch_arch",
229                           Twine('"') + ArchName + Twine('"'));
230     }
231   } else {
232     Builder.defineMacro("__loongarch_arch", Twine('"') + ArchName + Twine('"'));
233   }
234 
235   // Define __loongarch_tune.
236   StringRef TuneCPU = getTargetOpts().TuneCPU;
237   if (TuneCPU.empty())
238     TuneCPU = ArchName;
239   Builder.defineMacro("__loongarch_tune", Twine('"') + TuneCPU + Twine('"'));
240 
241   if (HasFeatureLASX) {
242     Builder.defineMacro("__loongarch_simd_width", "256");
243     Builder.defineMacro("__loongarch_sx", Twine(1));
244     Builder.defineMacro("__loongarch_asx", Twine(1));
245   } else if (HasFeatureLSX) {
246     Builder.defineMacro("__loongarch_simd_width", "128");
247     Builder.defineMacro("__loongarch_sx", Twine(1));
248   }
249   if (HasFeatureFrecipe)
250     Builder.defineMacro("__loongarch_frecipe", Twine(1));
251 
252   if (HasFeatureLAM_BH)
253     Builder.defineMacro("__loongarch_lam_bh", Twine(1));
254 
255   if (HasFeatureLAMCAS)
256     Builder.defineMacro("__loongarch_lamcas", Twine(1));
257 
258   if (HasFeatureLD_SEQ_SA)
259     Builder.defineMacro("__loongarch_ld_seq_sa", Twine(1));
260 
261   if (HasFeatureDiv32)
262     Builder.defineMacro("__loongarch_div32", Twine(1));
263 
264   if (HasFeatureSCQ)
265     Builder.defineMacro("__loongarch_scq", Twine(1));
266 
267   StringRef ABI = getABI();
268   if (ABI == "lp64d" || ABI == "lp64f" || ABI == "lp64s")
269     Builder.defineMacro("__loongarch_lp64");
270 
271   if (ABI == "lp64d" || ABI == "ilp32d") {
272     Builder.defineMacro("__loongarch_hard_float");
273     Builder.defineMacro("__loongarch_double_float");
274   } else if (ABI == "lp64f" || ABI == "ilp32f") {
275     Builder.defineMacro("__loongarch_hard_float");
276     Builder.defineMacro("__loongarch_single_float");
277   } else if (ABI == "lp64s" || ABI == "ilp32s") {
278     Builder.defineMacro("__loongarch_soft_float");
279   }
280 
281   Builder.defineMacro("__GCC_HAVE_SYNC_COMPARE_AND_SWAP_1");
282   Builder.defineMacro("__GCC_HAVE_SYNC_COMPARE_AND_SWAP_2");
283   Builder.defineMacro("__GCC_HAVE_SYNC_COMPARE_AND_SWAP_4");
284   if (GRLen == 64)
285     Builder.defineMacro("__GCC_HAVE_SYNC_COMPARE_AND_SWAP_8");
286 }
287 
288 static constexpr int NumBaseBuiltins =
289     LoongArch::FirstLSXBuiltin - Builtin::FirstTSBuiltin;
290 static constexpr int NumLSXBuiltins =
291     LoongArch::FirstLASXBuiltin - LoongArch::FirstLSXBuiltin;
292 static constexpr int NumLASXBuiltins =
293     LoongArch::LastTSBuiltin - LoongArch::FirstLASXBuiltin;
294 static constexpr int NumBuiltins =
295     LoongArch::LastTSBuiltin - Builtin::FirstTSBuiltin;
296 static_assert(NumBuiltins ==
297               (NumBaseBuiltins + NumLSXBuiltins + NumLASXBuiltins));
298 
299 static constexpr llvm::StringTable BuiltinBaseStrings =
300     CLANG_BUILTIN_STR_TABLE_START
301 #define TARGET_BUILTIN CLANG_TARGET_BUILTIN_STR_TABLE
302 #include "clang/Basic/BuiltinsLoongArchBase.def"
303 #undef TARGET_BUILTIN
304     ;
305 
306 static constexpr auto BuiltinBaseInfos = Builtin::MakeInfos<NumBaseBuiltins>({
307 #define TARGET_BUILTIN CLANG_TARGET_BUILTIN_ENTRY
308 #include "clang/Basic/BuiltinsLoongArchBase.def"
309 #undef TARGET_BUILTIN
310 });
311 
312 static constexpr llvm::StringTable BuiltinLSXStrings =
313     CLANG_BUILTIN_STR_TABLE_START
314 #define TARGET_BUILTIN CLANG_TARGET_BUILTIN_STR_TABLE
315 #include "clang/Basic/BuiltinsLoongArchLSX.def"
316 #undef TARGET_BUILTIN
317     ;
318 
319 static constexpr auto BuiltinLSXInfos = Builtin::MakeInfos<NumLSXBuiltins>({
320 #define TARGET_BUILTIN CLANG_TARGET_BUILTIN_ENTRY
321 #include "clang/Basic/BuiltinsLoongArchLSX.def"
322 #undef TARGET_BUILTIN
323 });
324 
325 static constexpr llvm::StringTable BuiltinLASXStrings =
326     CLANG_BUILTIN_STR_TABLE_START
327 #define TARGET_BUILTIN CLANG_TARGET_BUILTIN_STR_TABLE
328 #include "clang/Basic/BuiltinsLoongArchLASX.def"
329 #undef TARGET_BUILTIN
330     ;
331 
332 static constexpr auto BuiltinLASXInfos = Builtin::MakeInfos<NumLASXBuiltins>({
333 #define TARGET_BUILTIN CLANG_TARGET_BUILTIN_ENTRY
334 #include "clang/Basic/BuiltinsLoongArchLASX.def"
335 #undef TARGET_BUILTIN
336 });
337 
initFeatureMap(llvm::StringMap<bool> & Features,DiagnosticsEngine & Diags,StringRef CPU,const std::vector<std::string> & FeaturesVec) const338 bool LoongArchTargetInfo::initFeatureMap(
339     llvm::StringMap<bool> &Features, DiagnosticsEngine &Diags, StringRef CPU,
340     const std::vector<std::string> &FeaturesVec) const {
341   if (getTriple().getArch() == llvm::Triple::loongarch64)
342     Features["64bit"] = true;
343   if (getTriple().getArch() == llvm::Triple::loongarch32)
344     Features["32bit"] = true;
345 
346   return TargetInfo::initFeatureMap(Features, Diags, CPU, FeaturesVec);
347 }
348 
349 /// Return true if has this feature.
hasFeature(StringRef Feature) const350 bool LoongArchTargetInfo::hasFeature(StringRef Feature) const {
351   bool Is64Bit = getTriple().getArch() == llvm::Triple::loongarch64;
352   // TODO: Handle more features.
353   return llvm::StringSwitch<bool>(Feature)
354       .Case("loongarch32", !Is64Bit)
355       .Case("loongarch64", Is64Bit)
356       .Case("32bit", !Is64Bit)
357       .Case("64bit", Is64Bit)
358       .Case("lsx", HasFeatureLSX)
359       .Case("lasx", HasFeatureLASX)
360       .Default(false);
361 }
362 
363 llvm::SmallVector<Builtin::InfosShard>
getTargetBuiltins() const364 LoongArchTargetInfo::getTargetBuiltins() const {
365   return {
366       {&BuiltinBaseStrings, BuiltinBaseInfos},
367       {&BuiltinLSXStrings, BuiltinLSXInfos},
368       {&BuiltinLASXStrings, BuiltinLASXInfos},
369   };
370 }
371 
handleTargetFeatures(std::vector<std::string> & Features,DiagnosticsEngine & Diags)372 bool LoongArchTargetInfo::handleTargetFeatures(
373     std::vector<std::string> &Features, DiagnosticsEngine &Diags) {
374   for (const auto &Feature : Features) {
375     if (Feature == "+d" || Feature == "+f") {
376       // "d" implies "f".
377       HasFeatureF = true;
378       if (Feature == "+d") {
379         HasFeatureD = true;
380       }
381     } else if (Feature == "+lsx")
382       HasFeatureLSX = true;
383     else if (Feature == "+lasx")
384       HasFeatureLASX = true;
385     else if (Feature == "-ual")
386       HasUnalignedAccess = false;
387     else if (Feature == "+frecipe")
388       HasFeatureFrecipe = true;
389     else if (Feature == "+lam-bh")
390       HasFeatureLAM_BH = true;
391     else if (Feature == "+lamcas")
392       HasFeatureLAMCAS = true;
393     else if (Feature == "+ld-seq-sa")
394       HasFeatureLD_SEQ_SA = true;
395     else if (Feature == "+div32")
396       HasFeatureDiv32 = true;
397     else if (Feature == "+scq")
398       HasFeatureSCQ = true;
399   }
400   return true;
401 }
402 
403 enum class AttrFeatureKind { Arch, Tune, NoFeature, Feature };
404 
405 static std::pair<AttrFeatureKind, llvm::StringRef>
getAttrFeatureTypeAndValue(llvm::StringRef AttrFeature)406 getAttrFeatureTypeAndValue(llvm::StringRef AttrFeature) {
407   if (auto Split = AttrFeature.split("="); !Split.second.empty()) {
408     if (Split.first.trim() == "arch")
409       return {AttrFeatureKind::Arch, Split.second.trim()};
410     if (Split.first.trim() == "tune")
411       return {AttrFeatureKind::Tune, Split.second.trim()};
412   }
413   if (AttrFeature.starts_with("no-"))
414     return {AttrFeatureKind::NoFeature, AttrFeature.drop_front(3)};
415   return {AttrFeatureKind::Feature, AttrFeature};
416 }
417 
418 ParsedTargetAttr
parseTargetAttr(StringRef Features) const419 LoongArchTargetInfo::parseTargetAttr(StringRef Features) const {
420   ParsedTargetAttr Ret;
421   if (Features == "default")
422     return Ret;
423   SmallVector<StringRef, 1> AttrFeatures;
424   Features.split(AttrFeatures, ",");
425 
426   for (auto &Feature : AttrFeatures) {
427     auto [Kind, Value] = getAttrFeatureTypeAndValue(Feature.trim());
428 
429     switch (Kind) {
430     case AttrFeatureKind::Arch: {
431       if (llvm::LoongArch::isValidArchName(Value) || Value == "la64v1.0" ||
432           Value == "la64v1.1") {
433         std::vector<llvm::StringRef> ArchFeatures;
434         if (llvm::LoongArch::getArchFeatures(Value, ArchFeatures)) {
435           Ret.Features.insert(Ret.Features.end(), ArchFeatures.begin(),
436                               ArchFeatures.end());
437         }
438 
439         if (!Ret.CPU.empty())
440           Ret.Duplicate = "arch=";
441         else if (Value == "la64v1.0" || Value == "la64v1.1")
442           Ret.CPU = "loongarch64";
443         else
444           Ret.CPU = Value;
445       } else {
446         Ret.Features.push_back("!arch=" + Value.str());
447       }
448       break;
449     }
450 
451     case AttrFeatureKind::Tune:
452       if (!Ret.Tune.empty())
453         Ret.Duplicate = "tune=";
454       else
455         Ret.Tune = Value;
456       break;
457 
458     case AttrFeatureKind::NoFeature:
459       Ret.Features.push_back("-" + Value.str());
460       break;
461 
462     case AttrFeatureKind::Feature:
463       Ret.Features.push_back("+" + Value.str());
464       if (Value == "lasx")
465         Ret.Features.push_back("+lsx");
466       break;
467     }
468   }
469   return Ret;
470 }
471 
isValidCPUName(StringRef Name) const472 bool LoongArchTargetInfo::isValidCPUName(StringRef Name) const {
473   return llvm::LoongArch::isValidCPUName(Name);
474 }
475 
fillValidCPUList(SmallVectorImpl<StringRef> & Values) const476 void LoongArchTargetInfo::fillValidCPUList(
477     SmallVectorImpl<StringRef> &Values) const {
478   llvm::LoongArch::fillValidCPUList(Values);
479 }
480 
isValidFeatureName(StringRef Name) const481 bool LoongArchTargetInfo::isValidFeatureName(StringRef Name) const {
482   return llvm::LoongArch::isValidFeatureName(Name);
483 }
484