1 /*
2  * Copyright (c) 2021-2024 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_ARCH_H
17 #define PANDA_ARCH_H
18 
19 #include "macros.h"
20 #include "utils/math_helpers.h"
21 #include "utils/regmask.h"
22 #include "concepts.h"
23 
24 namespace ark {
25 
26 // NOLINTNEXTLINE(cppcoreguidelines-macro-usage)
27 #define ARCH_LIST(D) \
28     D(NONE)          \
29     D(AARCH32)       \
30     D(AARCH64)       \
31     D(X86)           \
32     D(X86_64)
33 
34 enum class Arch {
35 // NOLINTNEXTLINE(cppcoreguidelines-macro-usage)
36 #define DEF(v) v,
37     ARCH_LIST(DEF)
38 #undef DEF
39 };
40 
41 template <Arch ARCH>
42 struct ArchTraits;
43 
44 template <>
45 struct ArchTraits<Arch::AARCH32> {
46     static constexpr size_t CODE_ALIGNMENT = 8;
47     static constexpr size_t INSTRUCTION_ALIGNMENT = 4;
48     static constexpr size_t INSTRUCTION_MAX_SIZE_BITS = 32;
49     static constexpr size_t POINTER_SIZE = 4;
50     static constexpr bool IS_64_BITS = false;
51     static constexpr size_t THREAD_REG = 10;
52     static constexpr size_t CALLER_REG_MASK = 0x0000000f;
53     static constexpr size_t CALLER_FP_REG_MASK = 0x0000ffff;  // s0-s15 or d0-d7
54     static constexpr size_t CALLEE_REG_MASK = 0x000007f0;
55     static constexpr size_t CALLEE_FP_REG_MASK = 0xffff0000;  // s16-s31 or d8-d15
56     static constexpr bool SUPPORT_OSR = false;
57     static constexpr bool SUPPORT_DEOPTIMIZATION = true;
58     static constexpr const char *ISA_NAME = "arm";
59     static constexpr size_t DWARF_SP = 13;
60     static constexpr size_t DWARF_RIP = 15;
61     static constexpr size_t DWARF_FP = 11;
62     static constexpr size_t DWARF_LR = 14;
63     using WordType = uint32_t;
64 };
65 
ChooseIrtocOptimizedFpRegmask()66 inline constexpr size_t ChooseIrtocOptimizedFpRegmask()
67 {
68     constexpr size_t DEFAULT_FP_REGMASK = 0x00000100;
69     size_t regmask = DEFAULT_FP_REGMASK;
70 #include "plugins_regmasks.inl"
71     return regmask;
72 }
73 
74 template <>
75 struct ArchTraits<Arch::AARCH64> {
76     static constexpr size_t CODE_ALIGNMENT = 16;
77     static constexpr size_t INSTRUCTION_ALIGNMENT = 4;
78     static constexpr size_t INSTRUCTION_MAX_SIZE_BITS = 32;
79     static constexpr size_t POINTER_SIZE = 8;
80     static constexpr bool IS_64_BITS = true;
81     static constexpr size_t THREAD_REG = 28;
82     static constexpr size_t CALLER_REG_MASK = 0x0007ffff;
83     static constexpr size_t CALLER_FP_REG_MASK = 0xffff00ff;
84     static constexpr size_t CALLEE_REG_MASK = 0x1ff80000;
85     static constexpr size_t CALLEE_FP_REG_MASK = 0x0000ff00;
86     static constexpr size_t IRTOC_OPTIMIZED_CALLEE_REG_MASK = 0x1ff80000;
87     static constexpr size_t IRTOC_OPTIMIZED_CALLEE_FP_REG_MASK = ChooseIrtocOptimizedFpRegmask();
88     static constexpr bool SUPPORT_OSR = true;
89     static constexpr bool SUPPORT_DEOPTIMIZATION = true;
90     static constexpr const char *ISA_NAME = "arm64";
91     static constexpr size_t DWARF_SP = 31;
92     static constexpr size_t DWARF_RIP = 32;
93     static constexpr size_t DWARF_FP = 29;
94     static constexpr size_t DWARF_LR = 30;
95     using WordType = uint64_t;
96 };
97 
98 template <>
99 struct ArchTraits<Arch::X86> {
100     static constexpr size_t CODE_ALIGNMENT = 16;
101     static constexpr size_t INSTRUCTION_ALIGNMENT = 1;
102     static constexpr size_t INSTRUCTION_MAX_SIZE_BITS = 8;
103     static constexpr size_t POINTER_SIZE = 4;
104     static constexpr bool IS_64_BITS = false;
105     static constexpr size_t THREAD_REG = 0;
106     static constexpr size_t CALLER_REG_MASK = 0x00000000;
107     static constexpr size_t CALLER_FP_REG_MASK = 0x00000000;
108     static constexpr size_t CALLEE_REG_MASK = 0x00000001;
109     static constexpr size_t CALLEE_FP_REG_MASK =
110         0x00000001;  // NOTE(msherstennikov): fill once x86 codegen is supported
111     static constexpr bool SUPPORT_OSR = false;
112     static constexpr bool SUPPORT_DEOPTIMIZATION = false;
113     static constexpr const char *ISA_NAME = "x86";
114     static constexpr size_t DWARF_SP = 0;
115     static constexpr size_t DWARF_RIP = 0;
116     static constexpr size_t DWARF_FP = 0;
117     static constexpr size_t DWARF_LR = 0;
118     using WordType = uint32_t;
119 };
120 
121 template <>
122 struct ArchTraits<Arch::X86_64> {
123     static constexpr size_t CODE_ALIGNMENT = 16;
124     static constexpr size_t INSTRUCTION_ALIGNMENT = 1;
125     static constexpr size_t INSTRUCTION_MAX_SIZE_BITS = 8;
126     static constexpr size_t POINTER_SIZE = 8;
127     static constexpr bool IS_64_BITS = true;
128     static constexpr size_t THREAD_REG = 15;               // %r15
129     static constexpr size_t CALLER_REG_MASK = 0x000001FF;  // %rax, %rcx, %rdx, %rsi, %rdi, %r8, %r9, %r10, %r11
130     static constexpr size_t CALLER_FP_REG_MASK = 0x0000FFFF;
131     static constexpr size_t CALLEE_REG_MASK = 0x0000F800;  // %rbx, %r12, %r13, %r14, %r15
132     static constexpr size_t CALLEE_FP_REG_MASK = 0x00000000;
133     static constexpr bool SUPPORT_OSR = false;
134     static constexpr bool SUPPORT_DEOPTIMIZATION = true;
135     static constexpr const char *ISA_NAME = "x86_64";
136     static constexpr size_t DWARF_SP = 7;
137     static constexpr size_t DWARF_RIP = 16;
138     static constexpr size_t DWARF_FP = 6;
139     static constexpr size_t DWARF_LR = 0;
140     using WordType = uint64_t;
141 };
142 
143 template <>
144 struct ArchTraits<Arch::NONE> {
145     static constexpr size_t CODE_ALIGNMENT = 0;
146     static constexpr size_t INSTRUCTION_ALIGNMENT = 0;
147     static constexpr size_t INSTRUCTION_MAX_SIZE_BITS = 1;
148     static constexpr size_t POINTER_SIZE = 0;
149     static constexpr bool IS_64_BITS = false;
150     static constexpr size_t CALLEE_REG_MASK = 0x00000000;
151     static constexpr size_t CALLEE_FP_REG_MASK = 0x00000000;
152     static constexpr const char *ISA_NAME = "";
153     static constexpr size_t DWARF_SP = 0;
154     static constexpr size_t DWARF_RIP = 0;
155     static constexpr size_t DWARF_FP = 0;
156     static constexpr size_t DWARF_LR = 0;
157     using WordType = void;
158 };
159 
160 // NOLINTNEXTLINE(cppcoreguidelines-macro-usage,-warnings-as-errors)
161 #define DEF_ARCH_PROPERTY_GETTER(func_name, property)                                                 \
162     constexpr std::remove_const_t<decltype(ArchTraits<Arch::AARCH64>::property)> func_name(Arch arch) \
163     {                                                                                                 \
164         ASSERT(arch != Arch::NONE);                                                                   \
165         if (arch == Arch::X86) {                                                                      \
166             return ArchTraits<Arch::X86>::property;                                                   \
167         }                                                                                             \
168         if (arch == Arch::X86_64) {                                                                   \
169             return ArchTraits<Arch::X86_64>::property;                                                \
170         }                                                                                             \
171         if (arch == Arch::AARCH32) {                                                                  \
172             return ArchTraits<Arch::AARCH32>::property;                                               \
173         }                                                                                             \
174         if (arch == Arch::AARCH64) {                                                                  \
175             return ArchTraits<Arch::AARCH64>::property;                                               \
176         }                                                                                             \
177         UNREACHABLE();                                                                                \
178     }
179 
180 DEF_ARCH_PROPERTY_GETTER(DoesArchSupportDeoptimization, SUPPORT_DEOPTIMIZATION)
181 DEF_ARCH_PROPERTY_GETTER(GetCodeAlignment, CODE_ALIGNMENT)
182 DEF_ARCH_PROPERTY_GETTER(GetInstructionAlignment, INSTRUCTION_ALIGNMENT)
183 DEF_ARCH_PROPERTY_GETTER(GetInstructionSizeBits, INSTRUCTION_MAX_SIZE_BITS)
184 DEF_ARCH_PROPERTY_GETTER(Is64BitsArch, IS_64_BITS)
185 DEF_ARCH_PROPERTY_GETTER(PointerSize, POINTER_SIZE)
186 DEF_ARCH_PROPERTY_GETTER(GetThreadReg, THREAD_REG)
187 // constant is needed for correct call libdwarf-library
188 DEF_ARCH_PROPERTY_GETTER(GetIsaName, ISA_NAME)
189 DEF_ARCH_PROPERTY_GETTER(GetDwarfSP, DWARF_SP)
190 DEF_ARCH_PROPERTY_GETTER(GetDwarfRIP, DWARF_RIP)
191 DEF_ARCH_PROPERTY_GETTER(GetDwarfFP, DWARF_FP)
192 DEF_ARCH_PROPERTY_GETTER(GetDwarfLR, DWARF_LR)
193 
GetArchString(Arch arch)194 constexpr const char *GetArchString(Arch arch)
195 {
196     switch (arch) {
197 // NOLINTNEXTLINE(cppcoreguidelines-macro-usage)
198 #define DEF(v)    \
199     case Arch::v: \
200         return #v;
201         ARCH_LIST(DEF)
202 #undef DEF
203         default:
204             UNREACHABLE();
205     }
206 }
207 
GetCallerRegsMask(Arch arch, bool isFp)208 constexpr RegMask GetCallerRegsMask(Arch arch, bool isFp)
209 {
210     switch (arch) {
211         case Arch::AARCH32:
212             return isFp ? ArchTraits<Arch::AARCH32>::CALLER_FP_REG_MASK : ArchTraits<Arch::AARCH32>::CALLER_REG_MASK;
213         case Arch::AARCH64:
214             return isFp ? ArchTraits<Arch::AARCH64>::CALLER_FP_REG_MASK : ArchTraits<Arch::AARCH64>::CALLER_REG_MASK;
215         case Arch::X86:
216             return isFp ? ArchTraits<Arch::X86>::CALLER_FP_REG_MASK : ArchTraits<Arch::X86>::CALLER_REG_MASK;
217         case Arch::X86_64:
218             return isFp ? ArchTraits<Arch::X86_64>::CALLER_FP_REG_MASK : ArchTraits<Arch::X86_64>::CALLER_REG_MASK;
219         default:
220             UNREACHABLE();
221     }
222 }
223 
GetCalleeRegsMask(Arch arch, bool isFp, bool irtocOptimized = false)224 constexpr RegMask GetCalleeRegsMask(Arch arch, bool isFp, bool irtocOptimized = false)
225 {
226     switch (arch) {
227         case Arch::AARCH32:
228             return isFp ? ArchTraits<Arch::AARCH32>::CALLEE_FP_REG_MASK : ArchTraits<Arch::AARCH32>::CALLEE_REG_MASK;
229         case Arch::AARCH64:
230             // return AARCH64Case(arch, isFp, irtocOptimized)
231             static_assert((~ArchTraits<Arch::AARCH64>::CALLEE_FP_REG_MASK &
232                            ArchTraits<Arch::AARCH64>::IRTOC_OPTIMIZED_CALLEE_FP_REG_MASK) == 0);
233             static_assert((~ArchTraits<Arch::AARCH64>::CALLEE_REG_MASK &
234                            ArchTraits<Arch::AARCH64>::IRTOC_OPTIMIZED_CALLEE_REG_MASK) == 0);
235             return isFp             ? irtocOptimized ? ArchTraits<Arch::AARCH64>::IRTOC_OPTIMIZED_CALLEE_FP_REG_MASK
236                                                      : ArchTraits<Arch::AARCH64>::CALLEE_FP_REG_MASK
237                    : irtocOptimized ? ArchTraits<Arch::AARCH64>::IRTOC_OPTIMIZED_CALLEE_REG_MASK
238                                     : ArchTraits<Arch::AARCH64>::CALLEE_REG_MASK;
239         case Arch::X86:
240             return isFp ? ArchTraits<Arch::X86>::CALLEE_FP_REG_MASK : ArchTraits<Arch::X86>::CALLEE_REG_MASK;
241         case Arch::X86_64:
242             return isFp ? ArchTraits<Arch::X86_64>::CALLEE_FP_REG_MASK : ArchTraits<Arch::X86_64>::CALLEE_REG_MASK;
243         default:
244             UNREACHABLE();
245     }
246 }
247 
GetFirstCalleeReg(Arch arch, bool isFp)248 inline constexpr size_t GetFirstCalleeReg(Arch arch, bool isFp)
249 {
250     if (arch == Arch::X86_64 && isFp) {
251         // in amd64 xmm regs are volatile, so we return first reg (1) > last reg(0) to imitate empty list;
252         // also number of registers = last reg (0) - first reg (1) + 1 == 0
253         return 1;
254     }
255 
256     return GetCalleeRegsMask(arch, isFp).GetMinRegister();
257 }
258 
GetLastCalleeReg(Arch arch, bool isFp)259 inline constexpr size_t GetLastCalleeReg(Arch arch, bool isFp)
260 {
261     if (arch == Arch::X86_64 && isFp) {
262         return 0;
263     }
264 
265     return GetCalleeRegsMask(arch, isFp).GetMaxRegister();
266 }
267 
GetCalleeRegsCount(Arch arch, bool isFp)268 inline constexpr size_t GetCalleeRegsCount(Arch arch, bool isFp)
269 {
270     return GetCalleeRegsMask(arch, isFp).Count();
271 }
272 
GetFirstCallerReg(Arch arch, bool isFp)273 inline constexpr size_t GetFirstCallerReg(Arch arch, bool isFp)
274 {
275     return GetCallerRegsMask(arch, isFp).GetMinRegister();
276 }
277 
GetLastCallerReg(Arch arch, bool isFp)278 inline constexpr size_t GetLastCallerReg(Arch arch, bool isFp)
279 {
280     return GetCallerRegsMask(arch, isFp).GetMaxRegister();
281 }
282 
GetCallerRegsCount(Arch arch, bool isFp)283 inline constexpr size_t GetCallerRegsCount(Arch arch, bool isFp)
284 {
285     return GetCallerRegsMask(arch, isFp).Count();
286 }
287 
GetRegsCount(Arch arch)288 inline constexpr size_t GetRegsCount(Arch arch)
289 {
290     return GetCalleeRegsCount(arch, false) + GetCalleeRegsCount(arch, true) + GetCallerRegsCount(arch, false) +
291            GetCallerRegsCount(arch, true);
292 }
293 
294 #ifdef PANDA_TARGET_ARM32
295 static constexpr Arch RUNTIME_ARCH = Arch::AARCH32;
296 #elif defined(PANDA_TARGET_ARM64)
297 static constexpr Arch RUNTIME_ARCH = Arch::AARCH64;
298 #elif defined(PANDA_TARGET_X86)
299 static constexpr Arch RUNTIME_ARCH = Arch::X86;
300 #elif defined(PANDA_TARGET_AMD64)
301 static constexpr Arch RUNTIME_ARCH = Arch::X86_64;
302 #else
303 static constexpr Arch RUNTIME_ARCH = Arch::NONE;
304 #endif
305 
306 template <class String = std::string>
GetArchFromString(const String &str)307 std::enable_if_t<is_stringable_v<String>, Arch> GetArchFromString(const String &str)
308 {
309     // NOTE(msherstennikov): implement using macro if "aarch64", "aarch32" and so on would be a proper choice
310     if (str == "arm64") {
311         return Arch::AARCH64;
312     }
313     if (str == "arm" || str == "arm32") {
314         return Arch::AARCH32;
315     }
316     if (str == "x86") {
317         return Arch::X86;
318     }
319     if (str == "x86_64" || str == "x64") {
320         return Arch::X86_64;
321     }
322     return Arch::NONE;
323 }
324 
325 template <class String = std::string>
GetStringFromArch(const Arch &arch)326 std::enable_if_t<is_stringable_v<String>, String> GetStringFromArch(const Arch &arch)
327 {
328     if (arch == Arch::AARCH64) {
329         return "arm64";
330     }
331     if (arch == Arch::AARCH32) {
332         return "arm";
333     }
334     if (arch == Arch::X86) {
335         return "x86";
336     }
337     if (arch == Arch::X86_64) {
338         return "x86_64";
339     }
340     return "none";
341 }
342 
343 }  // namespace ark
344 
345 #endif  // PANDA_ARCH_H
346