1 /**
2 * Copyright (c) 2021-2022 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16 #ifndef PANDA_TARGET_H
17 #define PANDA_TARGET_H
18
19 #include <string>
20 #include "operands.h"
21 #include "arch_info_gen.h"
22
23 namespace panda::compiler {
24
25 // There is a problem with callee/caller register numbers with amd64.
26 // For example, take a look at
27 // caller reg mask: 0000111111000111 and
28 // callee reg mask: 1111000000001000
29 // Stack walker requires this mask to be densed, so the decision is to
30 // rename regs number 3, 4, 5 to 11, 10, 9 (and vice versa).
31 // Resulting
32 // caller mask is 0000000111111111 and
33 // callee mask is 1111100000000000.
34
ConvertRegNumberX86(size_t reg_id)35 constexpr size_t ConvertRegNumberX86(size_t reg_id)
36 {
37 constexpr size_t RENAMING_MASK_3_5_OR_9_11 {0xE38};
38 constexpr size_t RENAMING_CONST {14U};
39
40 ASSERT(reg_id < MAX_NUM_REGS);
41 // NOLINTNEXTLINE(clang-analyzer-core.UndefinedBinaryOperatorResult)
42 if ((RENAMING_MASK_3_5_OR_9_11 & (size_t(1) << reg_id)) != 0) {
43 return RENAMING_CONST - reg_id;
44 }
45 return reg_id;
46 }
47
48 // NOLINTNEXTLINE(cppcoreguidelines-macro-usage)
49 #define DEFINE_NUMERIC_REGISTERS(REG) \
50 REG(0) \
51 REG(1) \
52 REG(2) \
53 REG(3) \
54 REG(4) \
55 REG(5) \
56 REG(6) \
57 REG(7) \
58 REG(8) \
59 REG(9) \
60 REG(10) \
61 REG(11) \
62 REG(12) \
63 REG(13) \
64 REG(14) \
65 REG(15) \
66 REG(16) \
67 REG(17) \
68 REG(18) \
69 REG(19) \
70 REG(20) \
71 REG(21) \
72 REG(22) \
73 REG(23) \
74 REG(24) \
75 REG(25) \
76 REG(26) \
77 REG(27) \
78 REG(28) \
79 REG(29) \
80 REG(30) \
81 REG(31)
82
83 // NOLINTNEXTLINE(cppcoreguidelines-macro-usage)
84 #define DEFINE_REG(name, reg) static constexpr uint8_t name = reg
85
86 struct ArchCallingConventionX86_64 {
87 // Following registers are swapped (see comment above for ConvertRegNumberX86):
88 // BX, SP, BP <==> R9, R10, R11
89 // NOLINTNEXTLINE(cppcoreguidelines-macro-usage)
90 #define X86_64_REGISTER_LIST(REG) \
91 REG(rax) /* 0 */ \
92 REG(rcx) /* 1 */ \
93 REG(rdx) /* 2 */ \
94 REG(r11) /* 3 */ \
95 REG(r10) /* 4 */ \
96 REG(r9) /* 5 */ \
97 REG(rsi) /* 6 */ \
98 REG(rdi) /* 7 */ \
99 REG(r8) /* 8 */ \
100 REG(rbp) /* 9 */ \
101 REG(rsp) /* 10 */ \
102 REG(rbx) /* 11 */ \
103 REG(r12) /* 12 */ \
104 REG(r13) /* 13 */ \
105 REG(r14) /* 14 */ \
106 REG(r15) /* 15 */
107
108 // NOLINTNEXTLINE(cppcoreguidelines-macro-usage)
109 #define DEF_ENUM(r) id_##r,
110 enum Id : uint8_t { X86_64_REGISTER_LIST(DEF_ENUM) id_count };
111 #undef DEF_ENUM
112
113 DEFINE_REG(SP_REG, id_rsp);
114 DEFINE_REG(FP_REG, id_rbp);
115 DEFINE_REG(LR_REG, INVALID_REG_ID);
116 DEFINE_REG(RETURN_REG, id_rax);
117 DEFINE_REG(FP_RETURN_REG, 0);
118 DEFINE_REG(ZERO_REG, INVALID_REG_ID);
119 static constexpr std::array<size_t, 6> CALL_PARAMS_REGS = {id_rdi, id_rsi, id_rdx, id_rcx, id_r8, id_r9};
120 static constexpr uint32_t TEMP_REGS_MASK = arch_info::x86_64::TEMP_REGS.to_ulong();
121 static constexpr uint32_t TEMP_VREGS_MASK = arch_info::x86_64::TEMP_FP_REGS.to_ulong();
122 static constexpr uint32_t GENERAL_REGS_MASK = MakeMaskByExcluding(id_count, SP_REG, FP_REG);
123 static constexpr uint32_t SP_ALIGNMENT = 16;
124
125 // NOLINTNEXTLINE(cppcoreguidelines-macro-usage)
126 #define DEF_REG_NAME(r) #r,
127 static constexpr std::array<const char *, id_count> REG_NAMES = {X86_64_REGISTER_LIST(DEF_REG_NAME)};
128 #undef DEF_REG_NAME
129
130 // NOLINTNEXTLINE(cppcoreguidelines-macro-usage)
131 #define DEF_FP_REG_NAME(r) "xmm" #r,
132 static constexpr std::array<const char *, 32> FP_REG_NAMES = {DEFINE_NUMERIC_REGISTERS(DEF_FP_REG_NAME)};
133 #undef DEF_FP_REG_NAME
GetRegNameArchCallingConventionX86_64134 static constexpr const char *GetRegName(size_t reg, bool is_fp)
135 {
136 ASSERT(reg < REG_NAMES.size() || is_fp);
137 ASSERT(reg < FP_REG_NAMES.size() || !is_fp);
138 return is_fp ? FP_REG_NAMES[reg] : REG_NAMES[reg];
139 }
140 };
141
142 struct ArchCallingConventionAarch64 {
143 DEFINE_REG(SP_REG, 63);
144 DEFINE_REG(FP_REG, 29);
145 DEFINE_REG(LR_REG, 30);
146 DEFINE_REG(RETURN_REG, 0);
147 DEFINE_REG(FP_RETURN_REG, 0);
148 DEFINE_REG(ZERO_REG, 31);
149 static constexpr std::array CALL_PARAMS_REGS = {0, 1, 2, 3, 4, 5, 6, 7};
150 static constexpr uint32_t TEMP_REGS_MASK = arch_info::arm64::TEMP_REGS.to_ulong();
151 static constexpr uint32_t TEMP_VREGS_MASK = arch_info::arm64::TEMP_FP_REGS.to_ulong();
152 static constexpr uint32_t GENERAL_REGS_MASK = MakeMaskByExcluding(32, SP_REG, FP_REG, ZERO_REG, LR_REG);
153 static constexpr uint32_t SP_ALIGNMENT = 16;
154
155 // NOLINTNEXTLINE(cppcoreguidelines-macro-usage)
156 #define DEF_FP_REG_NAME(r) "r" #r,
157 static constexpr std::array<const char *, 32> REG_NAMES = {DEFINE_NUMERIC_REGISTERS(DEF_FP_REG_NAME)};
158 #undef DEF_FP_REG_NAME
159
160 // NOLINTNEXTLINE(cppcoreguidelines-macro-usage)
161 #define DEF_FP_REG_NAME(r) "d" #r,
162 static constexpr std::array<const char *, 32> FP_REG_NAMES = {DEFINE_NUMERIC_REGISTERS(DEF_FP_REG_NAME)};
163 #undef DEF_FP_REG_NAME
164
GetRegNameArchCallingConventionAarch64165 static constexpr const char *GetRegName(size_t reg, bool is_fp)
166 {
167 ASSERT(reg < REG_NAMES.size() || is_fp);
168 ASSERT(reg < FP_REG_NAMES.size() || !is_fp);
169 return is_fp ? FP_REG_NAMES[reg] : REG_NAMES[reg];
170 }
171 };
172
173 struct ArchCallingConventionAarch32 {
174 DEFINE_REG(SP_REG, 13);
175 DEFINE_REG(FP_REG, 11);
176 DEFINE_REG(LR_REG, 14);
177 DEFINE_REG(RETURN_REG, 0);
178 DEFINE_REG(FP_RETURN_REG, 0);
179 DEFINE_REG(ZERO_REG, INVALID_REG_ID);
180 static constexpr std::array CALL_PARAMS_REGS = {0, 1, 2, 3};
181 static constexpr uint32_t TEMP_REGS_MASK = arch_info::arm32::TEMP_REGS.to_ulong();
182 static constexpr uint32_t TEMP_VREGS_MASK = arch_info::arm32::TEMP_FP_REGS.to_ulong();
183 static constexpr uint32_t GENERAL_REGS_MASK = MakeMaskByExcluding(16, SP_REG, FP_REG, LR_REG);
184 static constexpr uint32_t SP_ALIGNMENT = 8;
185
186 // NOLINTNEXTLINE(cppcoreguidelines-macro-usage)
187 #define DEF_FP_REG_NAME(r) "r" #r,
188 static constexpr std::array<const char *, 32> REG_NAMES = {DEFINE_NUMERIC_REGISTERS(DEF_FP_REG_NAME)};
189 #undef DEF_FP_REG_NAME
190
191 // NOLINTNEXTLINE(cppcoreguidelines-macro-usage)
192 #define DEF_FP_REG_NAME(r) "d" #r,
193 static constexpr std::array<const char *, 32> FP_REG_NAMES = {DEFINE_NUMERIC_REGISTERS(DEF_FP_REG_NAME)};
194 #undef DEF_FP_REG_NAME
195
GetRegNameArchCallingConventionAarch32196 static constexpr const char *GetRegName(size_t reg, bool is_fp)
197 {
198 ASSERT(reg < REG_NAMES.size() || is_fp);
199 ASSERT(reg < FP_REG_NAMES.size() || !is_fp);
200 return is_fp ? FP_REG_NAMES[reg] : REG_NAMES[reg];
201 }
202 };
203
204 struct ArchCallingConventionX86 {
205 DEFINE_REG(SP_REG, 0);
206 DEFINE_REG(FP_REG, 0);
207 DEFINE_REG(LR_REG, 0);
208 DEFINE_REG(RETURN_REG, 0);
209 DEFINE_REG(FP_RETURN_REG, 0);
210 DEFINE_REG(ZERO_REG, 0);
211 static constexpr std::array CALL_PARAMS_REGS = {0, 1, 2, 3, 4, 5, 6, 7};
212 static constexpr uint32_t TEMP_REGS_MASK = MakeMask(0);
213 static constexpr uint32_t TEMP_VREGS_MASK = MakeMask(0);
214 static constexpr uint32_t GENERAL_REGS_MASK = MakeMaskByExcluding(16, 0);
215 static constexpr uint32_t SP_ALIGNMENT = 8;
216
GetRegNameArchCallingConventionX86217 static constexpr const char *GetRegName([[maybe_unused]] size_t reg, [[maybe_unused]] bool is_fp)
218 {
219 return "not supported";
220 }
221 };
222
223 template <Arch arch>
224 struct ArchCallingConvention {
225 using Target = std::conditional_t<
226 arch == Arch::X86_64, ArchCallingConventionX86_64,
227 std::conditional_t<
228 arch == Arch::X86, ArchCallingConventionX86,
229 std::conditional_t<arch == Arch::AARCH64, ArchCallingConventionAarch64,
230 std::conditional_t<arch == Arch::AARCH32, ArchCallingConventionAarch32, void>>>>;
231
WordSizeArchCallingConvention232 static constexpr size_t WordSize()
233 {
234 return ArchTraits<arch>::IS_64_BITS ? sizeof(uint64_t) : sizeof(uint32_t);
235 }
GetReturnRegIdArchCallingConvention236 static constexpr size_t GetReturnRegId()
237 {
238 return Target::RETURN_REG;
239 }
GetReturnFpRegIdArchCallingConvention240 static constexpr size_t GetReturnFpRegId()
241 {
242 return Target::FP_RETURN_REG;
243 }
GetReturnRegArchCallingConvention244 static constexpr Reg GetReturnReg(TypeInfo type)
245 {
246 // NOLINTNEXTLINE(readability-braces-around-statements,bugprone-suspicious-semicolon)
247 if constexpr (arch == Arch::AARCH32) {
248 if (type.IsFloat()) {
249 #if PANDA_TARGET_ARM32_ABI_HARD
250 return Reg(GetReturnFpRegId(), (type == FLOAT64_TYPE) ? FLOAT64_TYPE : FLOAT32_TYPE);
251 #else
252 return Reg(GetReturnRegId(), (type == FLOAT64_TYPE) ? INT64_TYPE : INT32_TYPE);
253 #endif
254 }
255 return Reg(GetReturnRegId(), type);
256 }
257 return type.IsFloat() ? Reg(GetReturnFpRegId(), type) : Reg(GetReturnRegId(), type);
258 }
GetStackRegArchCallingConvention259 static constexpr Reg GetStackReg()
260 {
261 return Reg(Target::SP_REG, ArchTraits<arch>::IS_64_BITS ? INT64_TYPE : INT32_TYPE);
262 }
GetFrameRegArchCallingConvention263 static constexpr Reg GetFrameReg()
264 {
265 return Reg(Target::FP_REG, ArchTraits<arch>::IS_64_BITS ? INT64_TYPE : INT32_TYPE);
266 }
GetLinkRegArchCallingConvention267 static constexpr Reg GetLinkReg()
268 {
269 return Reg(Target::LR_REG, ArchTraits<arch>::IS_64_BITS ? INT64_TYPE : INT32_TYPE);
270 }
GetZeroRegArchCallingConvention271 static constexpr Reg GetZeroReg()
272 {
273 return Reg(Target::ZERO_REG, ArchTraits<arch>::IS_64_BITS ? INT64_TYPE : INT32_TYPE);
274 }
SupportLinkRegArchCallingConvention275 static constexpr bool SupportLinkReg()
276 {
277 return Target::LR_REG != INVALID_REG_ID;
278 }
SupportZeroRegArchCallingConvention279 static constexpr bool SupportZeroReg()
280 {
281 return Target::ZERO_REG != INVALID_REG_ID;
282 }
GetParamRegsCountArchCallingConvention283 static constexpr size_t GetParamRegsCount()
284 {
285 return Target::CALL_PARAMS_REGS.size();
286 }
GetParamRegIdArchCallingConvention287 static constexpr size_t GetParamRegId(size_t index)
288 {
289 ASSERT(index < GetParamRegsCount());
290 return Target::CALL_PARAMS_REGS[index];
291 }
GetSpAlignmentArchCallingConvention292 static constexpr size_t GetSpAlignment()
293 {
294 return Target::SP_ALIGNMENT;
295 }
GetParamRegsMaskArchCallingConvention296 static constexpr RegMask GetParamRegsMask()
297 {
298 return MakeMask(Target::CALL_PARAMS_REGS);
299 }
300
GetTempRegsMaskArchCallingConvention301 static constexpr RegMask GetTempRegsMask()
302 {
303 return RegMask(Target::TEMP_REGS_MASK);
304 }
GetTempVRegsMaskArchCallingConvention305 static constexpr RegMask GetTempVRegsMask()
306 {
307 return Target::TEMP_VREGS_MASK;
308 }
GetGeneralRegsMaskArchCallingConvention309 static constexpr RegMask GetGeneralRegsMask()
310 {
311 return Target::GENERAL_REGS_MASK;
312 }
GetGeneralVRegsMaskArchCallingConvention313 static constexpr RegMask GetGeneralVRegsMask()
314 {
315 return Target::GENERAL_REGS_MASK;
316 }
GetAvailableRegsMaskArchCallingConvention317 static constexpr RegMask GetAvailableRegsMask()
318 {
319 return GetGeneralRegsMask() & (~GetTempRegsMask()) & (~RegMask(1U << ArchTraits<arch>::THREAD_REG));
320 }
GetAvailableVRegsMaskArchCallingConvention321 static constexpr RegMask GetAvailableVRegsMask()
322 {
323 return RegMask(ArchTraits<arch>::CALLEE_FP_REG_MASK | ArchTraits<arch>::CALLER_FP_REG_MASK) &
324 ~GetTempVRegsMask();
325 }
326 };
327
328 class Target {
329 public:
Target(Arch arch)330 constexpr explicit Target(Arch arch) : arch_(arch) {}
331 ~Target() = default;
332 DEFAULT_MOVE_CTOR(Target)
333 DEFAULT_COPY_CTOR(Target)
334 NO_MOVE_OPERATOR(Target);
335 NO_COPY_OPERATOR(Target);
336
GetArch()337 constexpr Arch GetArch() const
338 {
339 return arch_;
340 }
341
Current()342 static constexpr Target Current()
343 {
344 return Target(RUNTIME_ARCH);
345 }
346
347 #undef TARGET_DEFINE_GETTER
348 // NOLINTNEXTLINE(cppcoreguidelines-macro-usage)
349 #define TARGET_DEFINE_GETTER(name) \
350 constexpr auto name() const \
351 { \
352 switch (arch_) { \
353 case Arch::X86_64: \
354 return ArchCallingConvention<Arch::X86_64>::name(); \
355 case Arch::X86: \
356 return ArchCallingConvention<Arch::X86>::name(); \
357 case Arch::AARCH64: \
358 return ArchCallingConvention<Arch::AARCH64>::name(); \
359 case Arch::AARCH32: \
360 return ArchCallingConvention<Arch::AARCH32>::name(); \
361 default: \
362 UNREACHABLE(); \
363 } \
364 }
365
366 TARGET_DEFINE_GETTER(WordSize);
367 TARGET_DEFINE_GETTER(GetReturnRegId);
368 TARGET_DEFINE_GETTER(GetReturnFpRegId);
369 TARGET_DEFINE_GETTER(GetStackReg);
370 TARGET_DEFINE_GETTER(GetFrameReg);
371 TARGET_DEFINE_GETTER(GetLinkReg);
372 TARGET_DEFINE_GETTER(SupportLinkReg);
373 TARGET_DEFINE_GETTER(GetZeroReg);
374 TARGET_DEFINE_GETTER(SupportZeroReg);
375 TARGET_DEFINE_GETTER(GetTempRegsMask);
376 TARGET_DEFINE_GETTER(GetTempVRegsMask);
377 TARGET_DEFINE_GETTER(GetGeneralRegsMask);
378 TARGET_DEFINE_GETTER(GetAvailableRegsMask);
379 TARGET_DEFINE_GETTER(GetAvailableVRegsMask);
380 TARGET_DEFINE_GETTER(GetParamRegsCount);
381 TARGET_DEFINE_GETTER(GetParamRegsMask);
382 TARGET_DEFINE_GETTER(GetSpAlignment);
383
384 #undef TARGET_DEFINE_GETTER_1_ARG
385 // NOLINTNEXTLINE(cppcoreguidelines-macro-usage)
386 #define TARGET_DEFINE_GETTER_1_ARG(name, arg_type) \
387 constexpr auto name(arg_type arg) const \
388 { \
389 switch (arch_) { \
390 case Arch::X86_64: \
391 return ArchCallingConvention<Arch::X86_64>::name(arg); \
392 case Arch::X86: \
393 return ArchCallingConvention<Arch::X86>::name(arg); \
394 case Arch::AARCH64: \
395 return ArchCallingConvention<Arch::AARCH64>::name(arg); \
396 case Arch::AARCH32: \
397 return ArchCallingConvention<Arch::AARCH32>::name(arg); \
398 default: \
399 UNREACHABLE(); \
400 } \
401 }
402 TARGET_DEFINE_GETTER_1_ARG(GetReturnReg, TypeInfo);
403 TARGET_DEFINE_GETTER_1_ARG(GetParamRegId, size_t);
404
GetReturnReg()405 constexpr Reg GetReturnReg() const
406 {
407 return GetReturnReg(GetPtrRegType());
408 }
409
GetReturnFpReg()410 constexpr Reg GetReturnFpReg() const
411 {
412 return GetReturnReg(Is64BitsArch(GetArch()) ? FLOAT64_TYPE : FLOAT32_TYPE);
413 }
414
GetPtrRegType()415 constexpr TypeInfo GetPtrRegType() const
416 {
417 return Is64BitsArch(GetArch()) ? INT64_TYPE : INT32_TYPE;
418 }
419
GetParamReg(size_t index,TypeInfo type)420 constexpr Reg GetParamReg(size_t index, TypeInfo type) const
421 {
422 return Reg(GetParamRegId(index), type);
423 }
424
GetParamReg(size_t index)425 constexpr Reg GetParamReg(size_t index) const
426 {
427 return Reg(GetParamRegId(index), GetPtrRegType());
428 }
429
GetParamRegsMask(size_t limit)430 constexpr RegMask GetParamRegsMask(size_t limit) const
431 {
432 size_t mask = 0;
433 for (size_t i = 0; i < limit; i++) {
434 mask |= 1UL << GetParamRegId(i);
435 }
436 return RegMask(mask);
437 }
438
439 // TODO(msherstennikov): Take into account register size
GetRegName(size_t reg,bool is_fp)440 std::string GetRegName(size_t reg, bool is_fp) const
441 {
442 switch (arch_) {
443 case Arch::X86_64:
444 return ArchCallingConventionX86_64::GetRegName(reg, is_fp);
445 case Arch::X86:
446 return ArchCallingConventionX86::GetRegName(reg, is_fp);
447 case Arch::AARCH64:
448 return ArchCallingConventionAarch64::GetRegName(reg, is_fp);
449 case Arch::AARCH32:
450 return ArchCallingConventionAarch32::GetRegName(reg, is_fp);
451 case Arch::NONE:
452 return "r" + std::to_string(reg);
453 default:
454 UNREACHABLE();
455 }
456 }
457
458 private:
459 const Arch arch_;
460 };
461
462 // Check that all getters can be evaluated in compile time.
463 // NOLINTNEXTLINE(cppcoreguidelines-macro-usage)
464 #define VERIFY_GETTER(name) \
465 static_assert(Target(Arch::X86_64).name() == Target(Arch::X86_64).name()); \
466 static_assert(Target(Arch::AARCH64).name() == Target(Arch::AARCH64).name());
467 VERIFY_GETTER(WordSize)
468 VERIFY_GETTER(GetReturnRegId)
469 VERIFY_GETTER(GetReturnFpRegId)
470 VERIFY_GETTER(GetStackReg)
471 VERIFY_GETTER(GetFrameReg)
472 VERIFY_GETTER(GetLinkReg)
473 VERIFY_GETTER(SupportLinkReg)
474 VERIFY_GETTER(GetZeroReg)
475 VERIFY_GETTER(SupportZeroReg)
476 VERIFY_GETTER(GetTempRegsMask)
477 VERIFY_GETTER(GetTempVRegsMask)
478 VERIFY_GETTER(GetGeneralRegsMask)
479 VERIFY_GETTER(GetAvailableRegsMask)
480 VERIFY_GETTER(GetAvailableVRegsMask)
481 VERIFY_GETTER(GetParamRegsCount)
482 VERIFY_GETTER(GetParamRegsMask)
483 VERIFY_GETTER(GetSpAlignment)
484 static_assert(Target(Arch::X86_64).GetParamReg(0) == Target(Arch::X86_64).GetParamReg(0));
485 static_assert(Target(Arch::AARCH64).GetParamReg(0) == Target(Arch::AARCH64).GetParamReg(0));
486
487 } // namespace panda::compiler
488
489 #endif // PANDA_TARGET_H
490