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