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_TARGET_H
17 #define PANDA_TARGET_H
18
19 #include "operands.h"
20 #include "arch_info_gen.h"
21 #include <string>
22
23 namespace ark::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 regId)35 constexpr size_t ConvertRegNumberX86(size_t regId)
36 {
37 constexpr size_t RENAMING_MASK_3_5_OR_9_11 {0xE38};
38 constexpr size_t RENAMING_CONST {14U};
39
40 ASSERT(regId < MAX_NUM_REGS);
41 // NOLINTNEXTLINE(clang-analyzer-core.UndefinedBinaryOperatorResult)
42 if ((RENAMING_MASK_3_5_OR_9_11 & (size_t(1) << regId)) != 0) {
43 return RENAMING_CONST - regId;
44 }
45 return regId;
46 }
47
48 // CC-OFFNXT(G.PRE.06) list generation
49 // NOLINTNEXTLINE(cppcoreguidelines-macro-usage)
50 #define DEFINE_NUMERIC_REGISTERS(REG) \
51 REG(0) \
52 REG(1) \
53 REG(2) \
54 REG(3) \
55 REG(4) \
56 REG(5) \
57 REG(6) \
58 REG(7) \
59 REG(8) \
60 REG(9) \
61 REG(10) \
62 REG(11) \
63 REG(12) \
64 REG(13) \
65 REG(14) \
66 REG(15) \
67 REG(16) \
68 REG(17) \
69 REG(18) \
70 REG(19) \
71 REG(20) \
72 REG(21) \
73 REG(22) \
74 REG(23) \
75 REG(24) \
76 REG(25) \
77 REG(26) \
78 REG(27) \
79 REG(28) \
80 REG(29) \
81 REG(30) \
82 REG(31)
83
84 // NOLINTNEXTLINE(cppcoreguidelines-macro-usage)
85 #define DEFINE_REG(name, reg) static constexpr uint8_t name = (reg)
86
87 struct ArchCallingConventionX8664 {
88 // Following registers are swapped (see comment above for ConvertRegNumberX86):
89 // BX, SP, BP <==> R9, R10, R11
90 // CC-OFFNXT(G.PRE.06) solid logic
91 // NOLINTNEXTLINE(cppcoreguidelines-macro-usage)
92 #define X86_64_REGISTER_LIST(REG) \
93 REG(rax) /* 0 */ \
94 REG(rcx) /* 1 */ \
95 REG(rdx) /* 2 */ \
96 REG(r11) /* 3 */ \
97 REG(r10) /* 4 */ \
98 REG(r9) /* 5 */ \
99 REG(rsi) /* 6 */ \
100 REG(rdi) /* 7 */ \
101 REG(r8) /* 8 */ \
102 REG(rbp) /* 9 */ \
103 REG(rsp) /* 10 */ \
104 REG(rbx) /* 11 */ \
105 REG(r12) /* 12 */ \
106 REG(r13) /* 13 */ \
107 REG(r14) /* 14 */ \
108 REG(r15) /* 15 */
109
110 // NOLINTNEXTLINE(cppcoreguidelines-macro-usage)
111 #define DEF_ENUM(r) id_##r,
112 enum Id : uint8_t { X86_64_REGISTER_LIST(DEF_ENUM) ID_COUNT };
113 #undef DEF_ENUM
114
115 DEFINE_REG(SP_REG, id_rsp);
116 DEFINE_REG(FP_REG, id_rbp);
117 DEFINE_REG(LR_REG, INVALID_REG_ID);
118 DEFINE_REG(RETURN_REG, id_rax);
119 DEFINE_REG(FP_RETURN_REG, 0);
120 DEFINE_REG(ZERO_REG, INVALID_REG_ID);
121 static constexpr std::array<size_t, 6> CALL_PARAMS_REGS = {id_rdi, id_rsi, id_rdx, id_rcx, id_r8, id_r9};
122 static constexpr uint32_t TEMP_REGS_MASK = arch_info::x86_64::TEMP_REGS.to_ulong();
123 static constexpr uint32_t TEMP_VREGS_MASK = arch_info::x86_64::TEMP_FP_REGS.to_ulong();
124 static constexpr uint32_t GENERAL_REGS_MASK = MakeMaskByExcluding(ID_COUNT, SP_REG, FP_REG);
125 static constexpr uint32_t SP_ALIGNMENT = 16;
126
127 // NOLINTNEXTLINE(cppcoreguidelines-macro-usage)
128 #define DEF_REG_NAME(r) #r,
129 static constexpr std::array<const char *, ID_COUNT> REG_NAMES = {X86_64_REGISTER_LIST(DEF_REG_NAME)};
130 #undef DEF_REG_NAME
131
132 // NOLINTNEXTLINE(cppcoreguidelines-macro-usage)
133 #define DEF_FP_REG_NAME_X86(r) "xmm" #r,
134 static constexpr std::array<const char *, 32> FP_REG_NAMES = {DEFINE_NUMERIC_REGISTERS(DEF_FP_REG_NAME_X86)};
135 #undef DEF_FP_REG_NAME_X86
GetRegNameArchCallingConventionX8664136 static constexpr const char *GetRegName(size_t reg, bool isFp)
137 {
138 ASSERT(reg < REG_NAMES.size() || isFp);
139 ASSERT(reg < FP_REG_NAMES.size() || !isFp);
140 return isFp ? FP_REG_NAMES[reg] : REG_NAMES[reg];
141 }
142 };
143
144 struct ArchCallingConventionAarch64 {
145 DEFINE_REG(SP_REG, 63);
146 DEFINE_REG(FP_REG, 29);
147 DEFINE_REG(LR_REG, 30);
148 DEFINE_REG(RETURN_REG, 0);
149 DEFINE_REG(FP_RETURN_REG, 0);
150 DEFINE_REG(ZERO_REG, 31);
151 static constexpr std::array CALL_PARAMS_REGS = {0, 1, 2, 3, 4, 5, 6, 7};
152 static constexpr uint32_t TEMP_REGS_MASK = arch_info::arm64::TEMP_REGS.to_ulong();
153 static constexpr uint32_t TEMP_VREGS_MASK = arch_info::arm64::TEMP_FP_REGS.to_ulong();
154 static constexpr uint32_t GENERAL_REGS_MASK = MakeMaskByExcluding(32, SP_REG, FP_REG, ZERO_REG, LR_REG);
155 static constexpr uint32_t SP_ALIGNMENT = 16;
156
157 // NOLINTNEXTLINE(cppcoreguidelines-macro-usage)
158 #define DEF_FP_REG_NAME_AARCH_64(r) "r" #r,
159 static constexpr std::array<const char *, 32> REG_NAMES = {DEFINE_NUMERIC_REGISTERS(DEF_FP_REG_NAME_AARCH_64)};
160 #undef DEF_FP_REG_NAME_AARCH_64
161
162 // NOLINTNEXTLINE(cppcoreguidelines-macro-usage)
163 #define DEF_FP_REG_NAME_AARCH_64(r) "d" #r,
164 static constexpr std::array<const char *, 32> FP_REG_NAMES = {DEFINE_NUMERIC_REGISTERS(DEF_FP_REG_NAME_AARCH_64)};
165 #undef DEF_FP_REG_NAME_AARCH_64
166
GetRegNameArchCallingConventionAarch64167 static constexpr const char *GetRegName(size_t reg, bool isFp)
168 {
169 ASSERT(reg < REG_NAMES.size() || isFp);
170 ASSERT(reg < FP_REG_NAMES.size() || !isFp);
171 return isFp ? FP_REG_NAMES[reg] : REG_NAMES[reg];
172 }
173 };
174
175 struct ArchCallingConventionAarch32 {
176 DEFINE_REG(SP_REG, 13);
177 DEFINE_REG(FP_REG, 11);
178 DEFINE_REG(LR_REG, 14);
179 DEFINE_REG(RETURN_REG, 0);
180 DEFINE_REG(FP_RETURN_REG, 0);
181 DEFINE_REG(ZERO_REG, INVALID_REG_ID);
182 static constexpr std::array CALL_PARAMS_REGS = {0, 1, 2, 3};
183 static constexpr uint32_t TEMP_REGS_MASK = arch_info::arm32::TEMP_REGS.to_ulong();
184 static constexpr uint32_t TEMP_VREGS_MASK = arch_info::arm32::TEMP_FP_REGS.to_ulong();
185 static constexpr uint32_t GENERAL_REGS_MASK = MakeMaskByExcluding(16, SP_REG, FP_REG, LR_REG);
186 static constexpr uint32_t SP_ALIGNMENT = 8;
187
188 // NOLINTNEXTLINE(cppcoreguidelines-macro-usage)
189 #define DEF_FP_REG_NAME_AARCH_32(r) "r" #r,
190 static constexpr std::array<const char *, 32> REG_NAMES = {DEFINE_NUMERIC_REGISTERS(DEF_FP_REG_NAME_AARCH_32)};
191 #undef DEF_FP_REG_NAME_AARCH_32
192
193 // NOLINTNEXTLINE(cppcoreguidelines-macro-usage)
194 #define DEF_FP_REG_NAME_AARCH_32(r) "d" #r,
195 static constexpr std::array<const char *, 32> FP_REG_NAMES = {DEFINE_NUMERIC_REGISTERS(DEF_FP_REG_NAME_AARCH_32)};
196 #undef DEF_FP_REG_NAME_AARCH_32
197
GetRegNameArchCallingConventionAarch32198 static constexpr const char *GetRegName(size_t reg, bool isFp)
199 {
200 ASSERT(reg < REG_NAMES.size() || isFp);
201 ASSERT(reg < FP_REG_NAMES.size() || !isFp);
202 return isFp ? FP_REG_NAMES[reg] : REG_NAMES[reg];
203 }
204 };
205
206 struct ArchCallingConventionX86 {
207 DEFINE_REG(SP_REG, 0);
208 DEFINE_REG(FP_REG, 0);
209 DEFINE_REG(LR_REG, 0);
210 DEFINE_REG(RETURN_REG, 0);
211 DEFINE_REG(FP_RETURN_REG, 0);
212 DEFINE_REG(ZERO_REG, 0);
213 static constexpr std::array CALL_PARAMS_REGS = {0, 1, 2, 3, 4, 5, 6, 7};
214 static constexpr uint32_t TEMP_REGS_MASK = MakeMask(0);
215 static constexpr uint32_t TEMP_VREGS_MASK = MakeMask(0);
216 static constexpr uint32_t GENERAL_REGS_MASK = MakeMaskByExcluding(16, 0);
217 static constexpr uint32_t SP_ALIGNMENT = 8;
218
GetRegNameArchCallingConventionX86219 static constexpr const char *GetRegName([[maybe_unused]] size_t reg, [[maybe_unused]] bool isFp)
220 {
221 return "not supported";
222 }
223 };
224
225 template <Arch ARCH>
226 struct ArchCallingConvention {
227 using Target = std::conditional_t<
228 ARCH == Arch::X86_64, ArchCallingConventionX8664,
229 std::conditional_t<
230 ARCH == Arch::X86, ArchCallingConventionX86,
231 std::conditional_t<ARCH == Arch::AARCH64, ArchCallingConventionAarch64,
232 std::conditional_t<ARCH == Arch::AARCH32, ArchCallingConventionAarch32, void>>>>;
233
WordSizeArchCallingConvention234 static constexpr size_t WordSize()
235 {
236 return ArchTraits<ARCH>::IS_64_BITS ? sizeof(uint64_t) : sizeof(uint32_t);
237 }
GetReturnRegIdArchCallingConvention238 static constexpr size_t GetReturnRegId()
239 {
240 return Target::RETURN_REG;
241 }
GetReturnFpRegIdArchCallingConvention242 static constexpr size_t GetReturnFpRegId()
243 {
244 return Target::FP_RETURN_REG;
245 }
GetReturnRegArchCallingConvention246 static constexpr Reg GetReturnReg(TypeInfo type)
247 {
248 // NOLINTNEXTLINE(readability-braces-around-statements,bugprone-suspicious-semicolon)
249 if constexpr (ARCH == Arch::AARCH32) {
250 if (type.IsFloat()) {
251 #if PANDA_TARGET_ARM32_ABI_HARD
252 return Reg(GetReturnFpRegId(), (type == FLOAT64_TYPE) ? FLOAT64_TYPE : FLOAT32_TYPE);
253 #else
254 return Reg(GetReturnRegId(), (type == FLOAT64_TYPE) ? INT64_TYPE : INT32_TYPE);
255 #endif
256 }
257 return Reg(GetReturnRegId(), type);
258 }
259 return type.IsFloat() ? Reg(GetReturnFpRegId(), type) : Reg(GetReturnRegId(), type);
260 }
GetStackRegArchCallingConvention261 static constexpr Reg GetStackReg()
262 {
263 return Reg(Target::SP_REG, ArchTraits<ARCH>::IS_64_BITS ? INT64_TYPE : INT32_TYPE);
264 }
GetFrameRegArchCallingConvention265 static constexpr Reg GetFrameReg()
266 {
267 return Reg(Target::FP_REG, ArchTraits<ARCH>::IS_64_BITS ? INT64_TYPE : INT32_TYPE);
268 }
GetLinkRegArchCallingConvention269 static constexpr Reg GetLinkReg()
270 {
271 return Reg(Target::LR_REG, ArchTraits<ARCH>::IS_64_BITS ? INT64_TYPE : INT32_TYPE);
272 }
GetZeroRegArchCallingConvention273 static constexpr Reg GetZeroReg()
274 {
275 return Reg(Target::ZERO_REG, ArchTraits<ARCH>::IS_64_BITS ? INT64_TYPE : INT32_TYPE);
276 }
SupportLinkRegArchCallingConvention277 static constexpr bool SupportLinkReg()
278 {
279 return Target::LR_REG != INVALID_REG_ID;
280 }
SupportZeroRegArchCallingConvention281 static constexpr bool SupportZeroReg()
282 {
283 return Target::ZERO_REG != INVALID_REG_ID;
284 }
GetParamRegsCountArchCallingConvention285 static constexpr size_t GetParamRegsCount()
286 {
287 return Target::CALL_PARAMS_REGS.size();
288 }
GetParamRegIdArchCallingConvention289 static constexpr size_t GetParamRegId(size_t index)
290 {
291 ASSERT(index < GetParamRegsCount());
292 return Target::CALL_PARAMS_REGS[index];
293 }
GetSpAlignmentArchCallingConvention294 static constexpr size_t GetSpAlignment()
295 {
296 return Target::SP_ALIGNMENT;
297 }
GetParamRegsMaskArchCallingConvention298 static constexpr RegMask GetParamRegsMask()
299 {
300 return MakeMask(Target::CALL_PARAMS_REGS);
301 }
302
GetTempRegsMaskArchCallingConvention303 static constexpr RegMask GetTempRegsMask()
304 {
305 return RegMask(Target::TEMP_REGS_MASK);
306 }
GetTempVRegsMaskArchCallingConvention307 static constexpr RegMask GetTempVRegsMask()
308 {
309 return Target::TEMP_VREGS_MASK;
310 }
GetGeneralRegsMaskArchCallingConvention311 static constexpr RegMask GetGeneralRegsMask()
312 {
313 return Target::GENERAL_REGS_MASK;
314 }
GetGeneralVRegsMaskArchCallingConvention315 static constexpr RegMask GetGeneralVRegsMask()
316 {
317 return Target::GENERAL_REGS_MASK;
318 }
GetAvailableRegsMaskArchCallingConvention319 static constexpr RegMask GetAvailableRegsMask()
320 {
321 return GetGeneralRegsMask() & (~GetTempRegsMask());
322 }
GetAvailableVRegsMaskArchCallingConvention323 static constexpr RegMask GetAvailableVRegsMask()
324 {
325 return RegMask(ArchTraits<ARCH>::CALLEE_FP_REG_MASK | ArchTraits<ARCH>::CALLER_FP_REG_MASK) &
326 ~GetTempVRegsMask();
327 }
328 };
329
330 class Target {
331 public:
Target(Arch arch)332 constexpr explicit Target(Arch arch) : arch_(arch) {}
333 ~Target() = default;
334 DEFAULT_MOVE_CTOR(Target);
335 DEFAULT_COPY_CTOR(Target);
336 NO_MOVE_OPERATOR(Target);
337 NO_COPY_OPERATOR(Target);
338
GetArch()339 constexpr Arch GetArch() const
340 {
341 return arch_;
342 }
343
Current()344 static constexpr Target Current()
345 {
346 return Target(RUNTIME_ARCH);
347 }
348
349 #undef TARGET_DEFINE_GETTER
350 // CC-OFFNXT(G.PRE.06) solid logic
351 // NOLINTNEXTLINE(cppcoreguidelines-macro-usage)
352 #define TARGET_DEFINE_GETTER(name) \
353 constexpr auto name() const \
354 { \
355 switch (arch_) { \
356 case Arch::X86_64: \
357 /* CC-OFFNXT(G.PRE.05) function gen */ \
358 return ArchCallingConvention<Arch::X86_64>::name(); \
359 case Arch::X86: \
360 /* CC-OFFNXT(G.PRE.05) function gen */ \
361 return ArchCallingConvention<Arch::X86>::name(); \
362 case Arch::AARCH64: \
363 /* CC-OFFNXT(G.PRE.05) function gen */ \
364 return ArchCallingConvention<Arch::AARCH64>::name(); \
365 case Arch::AARCH32: \
366 /* CC-OFFNXT(G.PRE.05) function gen */ \
367 return ArchCallingConvention<Arch::AARCH32>::name(); \
368 default: \
369 UNREACHABLE(); \
370 } \
371 }
372
373 TARGET_DEFINE_GETTER(WordSize);
374 TARGET_DEFINE_GETTER(GetReturnRegId);
375 TARGET_DEFINE_GETTER(GetReturnFpRegId);
376 TARGET_DEFINE_GETTER(GetStackReg);
377 TARGET_DEFINE_GETTER(GetFrameReg);
378 TARGET_DEFINE_GETTER(GetLinkReg);
379 TARGET_DEFINE_GETTER(SupportLinkReg);
380 TARGET_DEFINE_GETTER(GetZeroReg);
381 TARGET_DEFINE_GETTER(SupportZeroReg);
382 TARGET_DEFINE_GETTER(GetTempRegsMask);
383 TARGET_DEFINE_GETTER(GetTempVRegsMask);
384 TARGET_DEFINE_GETTER(GetGeneralRegsMask);
385 TARGET_DEFINE_GETTER(GetAvailableRegsMask);
386 TARGET_DEFINE_GETTER(GetAvailableVRegsMask);
387 TARGET_DEFINE_GETTER(GetParamRegsCount);
388 TARGET_DEFINE_GETTER(GetParamRegsMask);
389 TARGET_DEFINE_GETTER(GetSpAlignment);
390
391 #undef TARGET_DEFINE_GETTER_1_ARG
392 // CC-OFFNXT(G.PRE.06) solid logic
393 // NOLINTNEXTLINE(cppcoreguidelines-macro-usage)
394 #define TARGET_DEFINE_GETTER_1_ARG(name, arg_type) \
395 constexpr auto name(arg_type arg) const \
396 { \
397 switch (arch_) { \
398 case Arch::X86_64: \
399 /* CC-OFFNXT(G.PRE.05) function gen */ \
400 return ArchCallingConvention<Arch::X86_64>::name(arg); \
401 case Arch::X86: \
402 /* CC-OFFNXT(G.PRE.05) function gen */ \
403 return ArchCallingConvention<Arch::X86>::name(arg); \
404 case Arch::AARCH64: \
405 /* CC-OFFNXT(G.PRE.05) function gen */ \
406 return ArchCallingConvention<Arch::AARCH64>::name(arg); \
407 case Arch::AARCH32: \
408 /* CC-OFFNXT(G.PRE.05) function gen */ \
409 return ArchCallingConvention<Arch::AARCH32>::name(arg); \
410 default: \
411 UNREACHABLE(); \
412 } \
413 }
414
415 TARGET_DEFINE_GETTER_1_ARG(GetReturnReg, TypeInfo);
416 TARGET_DEFINE_GETTER_1_ARG(GetParamRegId, size_t);
417
GetFpParamRegId(size_t index)418 constexpr size_t GetFpParamRegId(size_t index) const
419 {
420 switch (arch_) {
421 case Arch::X86_64:
422 case Arch::AARCH64:
423 case Arch::AARCH32:
424 return index;
425 default:
426 UNREACHABLE();
427 }
428 }
429
GetReturnReg()430 constexpr Reg GetReturnReg() const
431 {
432 return GetReturnReg(GetPtrRegType());
433 }
434
GetReturnFpReg()435 constexpr Reg GetReturnFpReg() const
436 {
437 return GetReturnReg(Is64BitsArch(GetArch()) ? FLOAT64_TYPE : FLOAT32_TYPE);
438 }
439
GetPtrRegType()440 constexpr TypeInfo GetPtrRegType() const
441 {
442 return Is64BitsArch(GetArch()) ? INT64_TYPE : INT32_TYPE;
443 }
444
GetParamReg(size_t index,TypeInfo type)445 constexpr Reg GetParamReg(size_t index, TypeInfo type) const
446 {
447 auto regId {type.IsScalar() ? GetParamRegId(index) : GetFpParamRegId(index)};
448 return Reg(regId, type);
449 }
450
GetParamReg(size_t index)451 constexpr Reg GetParamReg(size_t index) const
452 {
453 return Reg(GetParamRegId(index), GetPtrRegType());
454 }
455
GetParamRegsMask(size_t limit)456 constexpr RegMask GetParamRegsMask(size_t limit) const
457 {
458 size_t mask = 0;
459 for (size_t i = 0; i < limit; i++) {
460 mask |= 1UL << GetParamRegId(i);
461 }
462 return RegMask(mask);
463 }
464
465 // NOTE(msherstennikov): Take into account register size
GetRegName(size_t reg,bool isFp)466 std::string GetRegName(size_t reg, bool isFp) const
467 {
468 switch (arch_) {
469 case Arch::X86_64:
470 return ArchCallingConventionX8664::GetRegName(reg, isFp);
471 case Arch::X86:
472 return ArchCallingConventionX86::GetRegName(reg, isFp);
473 case Arch::AARCH64:
474 return ArchCallingConventionAarch64::GetRegName(reg, isFp);
475 case Arch::AARCH32:
476 return ArchCallingConventionAarch32::GetRegName(reg, isFp);
477 case Arch::NONE:
478 return "r" + std::to_string(reg);
479 default:
480 UNREACHABLE();
481 }
482 }
483
484 private:
485 const Arch arch_;
486 };
487
488 // Check that all getters can be evaluated in compile time.
489 // NOLINTNEXTLINE(cppcoreguidelines-macro-usage)
490 #define VERIFY_GETTER(name) \
491 static_assert(Target(Arch::X86_64).name() == Target(Arch::X86_64).name()); \
492 static_assert(Target(Arch::AARCH64).name() == Target(Arch::AARCH64).name())
493 VERIFY_GETTER(WordSize);
494 VERIFY_GETTER(GetReturnRegId);
495 VERIFY_GETTER(GetReturnFpRegId);
496 VERIFY_GETTER(GetStackReg);
497 VERIFY_GETTER(GetFrameReg);
498 VERIFY_GETTER(GetLinkReg);
499 VERIFY_GETTER(SupportLinkReg);
500 VERIFY_GETTER(GetZeroReg);
501 VERIFY_GETTER(SupportZeroReg);
502 VERIFY_GETTER(GetTempRegsMask);
503 VERIFY_GETTER(GetTempVRegsMask);
504 VERIFY_GETTER(GetGeneralRegsMask);
505 VERIFY_GETTER(GetAvailableRegsMask);
506 VERIFY_GETTER(GetAvailableVRegsMask);
507 VERIFY_GETTER(GetParamRegsCount);
508 VERIFY_GETTER(GetParamRegsMask);
509 VERIFY_GETTER(GetSpAlignment);
510 static_assert(Target(Arch::X86_64).GetParamReg(0) == Target(Arch::X86_64).GetParamReg(0));
511 static_assert(Target(Arch::AARCH64).GetParamReg(0) == Target(Arch::AARCH64).GetParamReg(0));
512
513 } // namespace ark::compiler
514
515 #endif // PANDA_TARGET_H
516