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 | |
20 | using namespace clang; |
21 | using namespace clang::targets; |
22 | |
23 | ArrayRef<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 | |
50 | ArrayRef<TargetInfo::GCCRegAlias> |
51 | LoongArchTargetInfo::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 | |
121 | bool 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 | |
169 | std::string |
170 | LoongArchTargetInfo::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 | |
186 | void 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 | Builder.defineMacro(Name: "__loongarch_arch" , Value: Twine('"') + ArchName + Twine('"')); |
204 | |
205 | // Define __loongarch_tune. |
206 | StringRef TuneCPU = getTargetOpts().TuneCPU; |
207 | if (TuneCPU.empty()) |
208 | TuneCPU = ArchName; |
209 | Builder.defineMacro(Name: "__loongarch_tune" , Value: Twine('"') + TuneCPU + Twine('"')); |
210 | |
211 | if (HasFeatureLSX) |
212 | Builder.defineMacro(Name: "__loongarch_sx" , Value: Twine(1)); |
213 | if (HasFeatureLASX) |
214 | Builder.defineMacro(Name: "__loongarch_asx" , Value: Twine(1)); |
215 | |
216 | StringRef ABI = getABI(); |
217 | if (ABI == "lp64d" || ABI == "lp64f" || ABI == "lp64s" ) |
218 | Builder.defineMacro(Name: "__loongarch_lp64" ); |
219 | |
220 | if (ABI == "lp64d" || ABI == "ilp32d" ) { |
221 | Builder.defineMacro(Name: "__loongarch_hard_float" ); |
222 | Builder.defineMacro(Name: "__loongarch_double_float" ); |
223 | } else if (ABI == "lp64f" || ABI == "ilp32f" ) { |
224 | Builder.defineMacro(Name: "__loongarch_hard_float" ); |
225 | Builder.defineMacro(Name: "__loongarch_single_float" ); |
226 | } else if (ABI == "lp64s" || ABI == "ilp32s" ) { |
227 | Builder.defineMacro(Name: "__loongarch_soft_float" ); |
228 | } |
229 | |
230 | Builder.defineMacro(Name: "__GCC_HAVE_SYNC_COMPARE_AND_SWAP_1" ); |
231 | Builder.defineMacro(Name: "__GCC_HAVE_SYNC_COMPARE_AND_SWAP_2" ); |
232 | Builder.defineMacro(Name: "__GCC_HAVE_SYNC_COMPARE_AND_SWAP_4" ); |
233 | if (GRLen == 64) |
234 | Builder.defineMacro(Name: "__GCC_HAVE_SYNC_COMPARE_AND_SWAP_8" ); |
235 | } |
236 | |
237 | static constexpr Builtin::Info BuiltinInfo[] = { |
238 | #define BUILTIN(ID, TYPE, ATTRS) \ |
239 | {#ID, TYPE, ATTRS, nullptr, HeaderDesc::NO_HEADER, ALL_LANGUAGES}, |
240 | #define TARGET_BUILTIN(ID, TYPE, ATTRS, FEATURE) \ |
241 | {#ID, TYPE, ATTRS, FEATURE, HeaderDesc::NO_HEADER, ALL_LANGUAGES}, |
242 | #include "clang/Basic/BuiltinsLoongArch.def" |
243 | }; |
244 | |
245 | bool LoongArchTargetInfo::initFeatureMap( |
246 | llvm::StringMap<bool> &Features, DiagnosticsEngine &Diags, StringRef CPU, |
247 | const std::vector<std::string> &FeaturesVec) const { |
248 | if (getTriple().getArch() == llvm::Triple::loongarch64) |
249 | Features["64bit" ] = true; |
250 | if (getTriple().getArch() == llvm::Triple::loongarch32) |
251 | Features["32bit" ] = true; |
252 | |
253 | return TargetInfo::initFeatureMap(Features, Diags, CPU, FeatureVec: FeaturesVec); |
254 | } |
255 | |
256 | /// Return true if has this feature. |
257 | bool LoongArchTargetInfo::hasFeature(StringRef Feature) const { |
258 | bool Is64Bit = getTriple().getArch() == llvm::Triple::loongarch64; |
259 | // TODO: Handle more features. |
260 | return llvm::StringSwitch<bool>(Feature) |
261 | .Case(S: "loongarch32" , Value: !Is64Bit) |
262 | .Case(S: "loongarch64" , Value: Is64Bit) |
263 | .Case(S: "32bit" , Value: !Is64Bit) |
264 | .Case(S: "64bit" , Value: Is64Bit) |
265 | .Case(S: "lsx" , Value: HasFeatureLSX) |
266 | .Case(S: "lasx" , Value: HasFeatureLASX) |
267 | .Default(Value: false); |
268 | } |
269 | |
270 | ArrayRef<Builtin::Info> LoongArchTargetInfo::getTargetBuiltins() const { |
271 | return llvm::ArrayRef(BuiltinInfo, clang::LoongArch::LastTSBuiltin - |
272 | Builtin::FirstTSBuiltin); |
273 | } |
274 | |
275 | bool LoongArchTargetInfo::handleTargetFeatures( |
276 | std::vector<std::string> &Features, DiagnosticsEngine &Diags) { |
277 | for (const auto &Feature : Features) { |
278 | if (Feature == "+d" || Feature == "+f" ) { |
279 | // "d" implies "f". |
280 | HasFeatureF = true; |
281 | if (Feature == "+d" ) { |
282 | HasFeatureD = true; |
283 | } |
284 | } else if (Feature == "+lsx" ) |
285 | HasFeatureLSX = true; |
286 | else if (Feature == "+lasx" ) |
287 | HasFeatureLASX = true; |
288 | else if (Feature == "-ual" ) |
289 | HasUnalignedAccess = false; |
290 | } |
291 | return true; |
292 | } |
293 | |
294 | bool LoongArchTargetInfo::isValidCPUName(StringRef Name) const { |
295 | return llvm::LoongArch::isValidCPUName(TuneCPU: Name); |
296 | } |
297 | |
298 | void LoongArchTargetInfo::fillValidCPUList( |
299 | SmallVectorImpl<StringRef> &Values) const { |
300 | llvm::LoongArch::fillValidCPUList(Values); |
301 | } |
302 | |