• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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