func.h (77111B)
1 // This file is part of AsmJit project <https://asmjit.com> 2 // 3 // See <asmjit/core.h> or LICENSE.md for license and copyright information 4 // SPDX-License-Identifier: Zlib 5 6 #ifndef ASMJIT_CORE_FUNC_H_INCLUDED 7 #define ASMJIT_CORE_FUNC_H_INCLUDED 8 9 #include "../core/archtraits.h" 10 #include "../core/environment.h" 11 #include "../core/operand.h" 12 #include "../core/type.h" 13 #include "../core/support.h" 14 15 ASMJIT_BEGIN_NAMESPACE 16 17 //! \addtogroup asmjit_function 18 //! \{ 19 20 //! Calling convention id. 21 //! 22 //! Calling conventions can be divided into the following groups: 23 //! 24 //! - Universal - calling conventions are applicable to any target. They will be converted to a target dependent 25 //! calling convention at runtime by \ref CallConv::init() with some help from \ref Environment. The purpose of 26 //! these calling conventions is to make using functions less target dependent and closer to C and C++. 27 //! 28 //! - Target specific - calling conventions that are used by a particular architecture and ABI. For example 29 //! Windows 64-bit calling convention and AMD64 SystemV calling convention. 30 enum class CallConvId : uint8_t { 31 // Universal Calling Conventions 32 // ----------------------------- 33 34 //! Standard function call or explicit `__cdecl` where it can be specified. 35 //! 36 //! This is a universal calling convention, which is used to initialize specific calling conventions based on 37 //! architecture, platform, and its ABI. 38 kCDecl = 0, 39 40 //! `__stdcall` on targets that support this calling convention (X86). 41 //! 42 //! \note This calling convention is only supported on 32-bit X86. If used on environment that doesn't support 43 //! this calling convention it will be replaced by \ref CallConvId::kCDecl. 44 kStdCall = 1, 45 46 //! `__fastcall` on targets that support this calling convention (X86). 47 //! 48 //! \note This calling convention is only supported on 32-bit X86. If used on environment that doesn't support 49 //! this calling convention it will be replaced by \ref CallConvId::kCDecl. 50 kFastCall = 2, 51 52 //! `__vectorcall` on targets that support this calling convention (X86|X86_64). 53 //! 54 //! \note This calling convention is only supported on 32-bit and 64-bit X86 architecture on Windows platform. 55 //! If used on environment that doesn't support this calling it will be replaced by \ref CallConvId::kCDecl. 56 kVectorCall = 3, 57 58 //! `__thiscall` on targets that support this calling convention (X86). 59 //! 60 //! \note This calling convention is only supported on 32-bit X86 Windows platform. If used on environment that 61 //! doesn't support this calling convention it will be replaced by \ref CallConvId::kCDecl. 62 kThisCall = 4, 63 64 //! `__attribute__((regparm(1)))` convention (GCC and Clang). 65 kRegParm1 = 5, 66 //! `__attribute__((regparm(2)))` convention (GCC and Clang). 67 kRegParm2 = 6, 68 //! `__attribute__((regparm(3)))` convention (GCC and Clang). 69 kRegParm3 = 7, 70 71 //! AsmJit specific calling convention designed for calling functions inside a multimedia code that don't use many 72 //! registers internally, but are long enough to be called and not inlined. These functions are usually used to 73 //! calculate trigonometric functions, logarithms, etc... 74 kLightCall2 = 16, 75 kLightCall3 = 17, 76 kLightCall4 = 18, 77 78 // ABI-Specific Calling Conventions 79 // -------------------------------- 80 81 //! Soft-float calling convention (AArch32). 82 //! 83 //! Floating point arguments are passed via general purpose registers. 84 kSoftFloat = 30, 85 86 //! Hard-float calling convention (AArch32). 87 //! 88 //! Floating point arguments are passed via SIMD registers. 89 kHardFloat = 31, 90 91 //! X64 System-V calling convention. 92 kX64SystemV = 32, 93 //! X64 Windows calling convention. 94 kX64Windows = 33, 95 96 //! Maximum value of `CallConvId`. 97 kMaxValue = kX64Windows 98 }; 99 100 //! Strategy used by calling conventions to assign registers to function arguments. 101 //! 102 //! Calling convention strategy describes how AsmJit should convert function arguments used by \ref FuncSignature 103 //! into register identifiers and stack offsets. The \ref CallConvStrategy::kDefault strategy assigns registers 104 //! and then stack whereas \ref CallConvStrategy::kX64Windows strategy does register shadowing as defined by WIN64 105 //! calling convention, which is only used by 64-bit Windows. 106 enum class CallConvStrategy : uint8_t { 107 //! Default register assignment strategy. 108 kDefault = 0, 109 //! Windows 64-bit ABI register assignment strategy. 110 kX64Windows = 1, 111 //! Windows 64-bit __vectorcall register assignment strategy. 112 kX64VectorCall = 2, 113 //! Apple's AArch64 calling convention (differs compared to AArch64 calling convention used by Linux). 114 kAArch64Apple = 3, 115 116 //! Maximum value of `CallConvStrategy`. 117 kMaxValue = kX64VectorCall 118 }; 119 120 //! Calling convention flags. 121 enum class CallConvFlags : uint32_t { 122 //! No flags. 123 kNone = 0, 124 //! Callee is responsible for cleaning up the stack. 125 kCalleePopsStack = 0x0001u, 126 //! Pass vector arguments indirectly (as a pointer). 127 kIndirectVecArgs = 0x0002u, 128 //! Pass F32 and F64 arguments via VEC128 register. 129 kPassFloatsByVec = 0x0004u, 130 //! Pass MMX and vector arguments via stack if the function has variable arguments. 131 kPassVecByStackIfVA = 0x0008u, 132 //! MMX registers are passed and returned via GP registers. 133 kPassMmxByGp = 0x0010u, 134 //! MMX registers are passed and returned via XMM registers. 135 kPassMmxByXmm = 0x0020u, 136 //! Calling convention can be used with variable arguments. 137 kVarArgCompatible = 0x0080u 138 }; 139 ASMJIT_DEFINE_ENUM_FLAGS(CallConvFlags) 140 141 //! Function calling convention. 142 //! 143 //! Function calling convention is a scheme that defines how function parameters are passed and how function 144 //! returns its result. AsmJit defines a variety of architecture and OS specific calling conventions and also 145 //! provides a compile time detection to make the code-generation easier. 146 struct CallConv { 147 //! \name Constants 148 //! \{ 149 150 //! Maximum number of register arguments per register group. 151 //! 152 //! \note This is not really AsmJit's limitation, it's just the number that makes sense considering all common 153 //! calling conventions. Usually even conventions that use registers to pass function arguments are limited to 8 154 //! and less arguments passed via registers per group. 155 static inline constexpr uint32_t kMaxRegArgsPerGroup = 16; 156 157 //! \} 158 159 //! \name Members 160 //! \{ 161 162 //! Target architecture. 163 Arch _arch; 164 //! Calling convention id. 165 CallConvId _id; 166 //! Register assignment strategy. 167 CallConvStrategy _strategy; 168 169 //! Red zone size (AMD64 == 128 bytes). 170 uint8_t _red_zone_size; 171 //! Spill zone size (WIN-X64 == 32 bytes). 172 uint8_t _spill_zone_size; 173 //! Natural stack alignment as defined by OS/ABI. 174 uint8_t _natural_stack_alignment; 175 176 //! \cond INTERNAL 177 //! Reserved for future use. 178 uint8_t _reserved[2]; 179 //! \endcond 180 181 //! Calling convention flags. 182 CallConvFlags _flags; 183 184 //! Size to save/restore per register group. 185 Support::Array<uint8_t, Globals::kNumVirtGroups> _save_restore_reg_size; 186 //! Alignment of save/restore groups. 187 Support::Array<uint8_t, Globals::kNumVirtGroups> _save_restore_alignment; 188 189 //! Mask of all passed registers, per group. 190 Support::Array<RegMask, Globals::kNumVirtGroups> _passed_regs; 191 //! Mask of all preserved registers, per group. 192 Support::Array<RegMask, Globals::kNumVirtGroups> _preserved_regs; 193 194 //! Passed registers' order. 195 union RegOrder { 196 //! Passed registers, ordered. 197 uint8_t id[kMaxRegArgsPerGroup]; 198 //! Packed IDs in `uint32_t` array. 199 uint32_t packed[(kMaxRegArgsPerGroup + 3) / 4]; 200 }; 201 202 //! Passed registers' order, per register group. 203 Support::Array<RegOrder, Globals::kNumVirtGroups> _passed_order; 204 205 //! \} 206 207 //! \name Construction & Destruction 208 //! \{ 209 210 //! Initializes this calling convention to the given `call_conv_id` based on the `environment`. 211 //! 212 //! See \ref CallConvId and \ref Environment for more details. 213 ASMJIT_API Error init(CallConvId call_conv_id, const Environment& environment) noexcept; 214 215 //! Resets this CallConv struct into a defined state. 216 //! 217 //! It's recommended to reset the \ref CallConv struct in case you would like create a custom calling convention 218 //! as it prevents from using an uninitialized data (CallConv doesn't have a constructor that would initialize it, 219 //! it's just a struct). 220 ASMJIT_INLINE_NODEBUG void reset() noexcept { 221 *this = CallConv{}; 222 memset(_passed_order.data(), 0xFF, sizeof(_passed_order)); 223 } 224 225 //! \} 226 227 //! \name Accessors 228 //! \{ 229 230 //! Returns the target architecture of this calling convention. 231 [[nodiscard]] 232 ASMJIT_INLINE_NODEBUG Arch arch() const noexcept { return _arch; } 233 234 //! Sets the target architecture of this calling convention. 235 ASMJIT_INLINE_NODEBUG void set_arch(Arch arch) noexcept { _arch = arch; } 236 237 //! Returns the calling convention id. 238 [[nodiscard]] 239 ASMJIT_INLINE_NODEBUG CallConvId id() const noexcept { return _id; } 240 241 //! Sets the calling convention id. 242 ASMJIT_INLINE_NODEBUG void set_id(CallConvId call_conv_id) noexcept { _id = call_conv_id; } 243 244 //! Returns the strategy used to assign registers to arguments. 245 [[nodiscard]] 246 ASMJIT_INLINE_NODEBUG CallConvStrategy strategy() const noexcept { return _strategy; } 247 248 //! Sets the strategy used to assign registers to arguments. 249 ASMJIT_INLINE_NODEBUG void set_strategy(CallConvStrategy strategy) noexcept { _strategy = strategy; } 250 251 //! Tests whether the calling convention has the given `flag` set. 252 [[nodiscard]] 253 ASMJIT_INLINE_NODEBUG bool has_flag(CallConvFlags flag) const noexcept { return Support::test(_flags, flag); } 254 255 //! Returns the calling convention flags, see `Flags`. 256 [[nodiscard]] 257 ASMJIT_INLINE_NODEBUG CallConvFlags flags() const noexcept { return _flags; } 258 259 //! Adds the calling convention flags, see `Flags`. 260 ASMJIT_INLINE_NODEBUG void set_flags(CallConvFlags flag) noexcept { _flags = flag; }; 261 262 //! Adds the calling convention flags, see `Flags`. 263 ASMJIT_INLINE_NODEBUG void add_flags(CallConvFlags flags) noexcept { _flags |= flags; }; 264 265 //! Tests whether this calling convention specifies 'Red Zone'. 266 [[nodiscard]] 267 ASMJIT_INLINE_NODEBUG bool has_red_zone() const noexcept { return _red_zone_size != 0; } 268 269 //! Tests whether this calling convention specifies 'Spill Zone'. 270 [[nodiscard]] 271 ASMJIT_INLINE_NODEBUG bool has_spill_zone() const noexcept { return _spill_zone_size != 0; } 272 273 //! Returns size of 'Red Zone'. 274 [[nodiscard]] 275 ASMJIT_INLINE_NODEBUG uint32_t red_zone_size() const noexcept { return _red_zone_size; } 276 277 //! Sets size of 'Red Zone'. 278 ASMJIT_INLINE_NODEBUG void set_red_zone_size(uint32_t size) noexcept { _red_zone_size = uint8_t(size); } 279 280 //! Returns size of 'Spill Zone'. 281 [[nodiscard]] 282 ASMJIT_INLINE_NODEBUG uint32_t spill_zone_size() const noexcept { return _spill_zone_size; } 283 284 //! Sets size of 'Spill Zone'. 285 ASMJIT_INLINE_NODEBUG void set_spill_zone_size(uint32_t size) noexcept { _spill_zone_size = uint8_t(size); } 286 287 //! Returns a natural stack alignment. 288 [[nodiscard]] 289 ASMJIT_INLINE_NODEBUG uint32_t natural_stack_alignment() const noexcept { return _natural_stack_alignment; } 290 291 //! Sets a natural stack alignment. 292 //! 293 //! This function can be used to override the default stack alignment in case that you know that it's alignment is 294 //! different. For example it allows to implement custom calling conventions that guarantee higher stack alignment. 295 ASMJIT_INLINE_NODEBUG void set_natural_stack_alignment(uint32_t value) noexcept { _natural_stack_alignment = uint8_t(value); } 296 297 //! Returns the size of a register (or its part) to be saved and restored of the given `group`. 298 [[nodiscard]] 299 ASMJIT_INLINE_NODEBUG uint32_t save_restore_reg_size(RegGroup group) const noexcept { return _save_restore_reg_size[group]; } 300 301 //! Sets the size of a vector register (or its part) to be saved and restored. 302 ASMJIT_INLINE_NODEBUG void set_save_restore_reg_size(RegGroup group, uint32_t size) noexcept { _save_restore_reg_size[group] = uint8_t(size); } 303 304 //! Returns the alignment of a save-restore area of the given `group`. 305 [[nodiscard]] 306 ASMJIT_INLINE_NODEBUG uint32_t save_restore_alignment(RegGroup group) const noexcept { return _save_restore_alignment[group]; } 307 308 //! Sets the alignment of a save-restore area of the given `group`. 309 ASMJIT_INLINE_NODEBUG void set_save_restore_alignment(RegGroup group, uint32_t alignment) noexcept { _save_restore_alignment[group] = uint8_t(alignment); } 310 311 //! Returns the order of passed registers of the given `group`. 312 [[nodiscard]] 313 ASMJIT_INLINE const uint8_t* passed_order(RegGroup group) const noexcept { 314 ASMJIT_ASSERT(group <= RegGroup::kMaxVirt); 315 return _passed_order[size_t(group)].id; 316 } 317 318 //! Returns the mask of passed registers of the given `group`. 319 [[nodiscard]] 320 ASMJIT_INLINE RegMask passed_regs(RegGroup group) const noexcept { 321 ASMJIT_ASSERT(group <= RegGroup::kMaxVirt); 322 return _passed_regs[size_t(group)]; 323 } 324 325 ASMJIT_INLINE void _set_passed_as_packed(RegGroup group, uint32_t p0, uint32_t p1, uint32_t p2, uint32_t p3) noexcept { 326 ASMJIT_ASSERT(group <= RegGroup::kMaxVirt); 327 328 _passed_order[group].packed[0] = p0; 329 _passed_order[group].packed[1] = p1; 330 _passed_order[group].packed[2] = p2; 331 _passed_order[group].packed[3] = p3; 332 } 333 334 //! Resets the order and mask of passed registers. 335 ASMJIT_INLINE void set_passed_to_none(RegGroup group) noexcept { 336 ASMJIT_ASSERT(group <= RegGroup::kMaxVirt); 337 338 _set_passed_as_packed(group, 0xFFFFFFFFu, 0xFFFFFFFFu, 0xFFFFFFFFu, 0xFFFFFFFFu); 339 _passed_regs[size_t(group)] = 0u; 340 } 341 342 //! Sets the order and mask of passed registers. 343 ASMJIT_INLINE void set_passed_order(RegGroup group, uint32_t a0, uint32_t a1 = 0xFF, uint32_t a2 = 0xFF, uint32_t a3 = 0xFF, uint32_t a4 = 0xFF, uint32_t a5 = 0xFF, uint32_t a6 = 0xFF, uint32_t a7 = 0xFF) noexcept { 344 ASMJIT_ASSERT(group <= RegGroup::kMaxVirt); 345 346 // NOTE: This should always be called with all arguments known at compile time, so even if it looks scary it 347 // should be translated into few instructions. 348 _set_passed_as_packed(group, Support::bytepack32_4x8(a0, a1, a2, a3), 349 Support::bytepack32_4x8(a4, a5, a6, a7), 350 0xFFFFFFFFu, 351 0xFFFFFFFFu); 352 353 _passed_regs[group] = (a0 != 0xFF ? 1u << a0 : 0u) | 354 (a1 != 0xFF ? 1u << a1 : 0u) | 355 (a2 != 0xFF ? 1u << a2 : 0u) | 356 (a3 != 0xFF ? 1u << a3 : 0u) | 357 (a4 != 0xFF ? 1u << a4 : 0u) | 358 (a5 != 0xFF ? 1u << a5 : 0u) | 359 (a6 != 0xFF ? 1u << a6 : 0u) | 360 (a7 != 0xFF ? 1u << a7 : 0u) ; 361 } 362 363 //! Returns preserved register mask of the given `group`. 364 [[nodiscard]] 365 ASMJIT_INLINE RegMask preserved_regs(RegGroup group) const noexcept { 366 ASMJIT_ASSERT(group <= RegGroup::kMaxVirt); 367 return _preserved_regs[group]; 368 } 369 370 //! Sets preserved register mask of the given `group`. 371 ASMJIT_INLINE void set_preserved_regs(RegGroup group, RegMask regs) noexcept { 372 ASMJIT_ASSERT(group <= RegGroup::kMaxVirt); 373 _preserved_regs[group] = regs; 374 } 375 376 //! \} 377 }; 378 379 //! Function signature. 380 //! 381 //! Contains information about a function return type, count of arguments, and their TypeIds. Function signature 382 //! is a low level structure which doesn't contain platform specific or calling convention specific information. 383 //! It's typically used to describe function arguments in a C-API like form, which is then used to calculate a 384 //! \ref FuncDetail instance, which then maps function signature into a platform and calling convention specific 385 //! format. 386 //! 387 //! Function signature can be built either dynamically by using \ref add_arg() and \ref add_arg_t() functionality, 388 //! or dynamically by using a template-based \ref FuncSignature::build() function, which maps template types 389 //! into a function signature. 390 struct FuncSignature { 391 //! \name Constants 392 //! \{ 393 394 //! Doesn't have variable number of arguments (`...`). 395 static inline constexpr uint8_t kNoVarArgs = 0xFFu; 396 397 //! \} 398 399 //! \name Members 400 //! \{ 401 402 //! Calling convention id. 403 CallConvId _call_conv_id = CallConvId::kCDecl; 404 //! Count of arguments. 405 uint8_t _arg_count = 0; 406 //! Index of a first VA or `kNoVarArgs`. 407 uint8_t _va_index = kNoVarArgs; 408 //! Return value TypeId. 409 TypeId _ret = TypeId::kVoid; 410 //! Reserved for future use. 411 uint8_t _reserved[4] {}; 412 //! Function argument TypeIds. 413 TypeId _args[Globals::kMaxFuncArgs] {}; 414 415 //! \} 416 417 //! \name Construction & Destruction 418 //! \{ 419 420 //! Default constructed function signature, initialized to \ref CallConvId::kCDecl, having no return value and no arguments. 421 ASMJIT_INLINE_CONSTEXPR FuncSignature() = default; 422 423 //! Copy constructor, which is initialized to the same function signature as `other`. 424 ASMJIT_INLINE_CONSTEXPR FuncSignature(const FuncSignature& other) = default; 425 426 //! Initializes the function signature with calling convention id `call_conv_id` and variable argument's index `va_index`. 427 ASMJIT_INLINE_CONSTEXPR FuncSignature(CallConvId call_conv_id, uint32_t va_index = kNoVarArgs) noexcept 428 : _call_conv_id(call_conv_id), 429 _va_index(uint8_t(va_index)) {} 430 431 //! Initializes the function signature with calling convention id `call_conv_id`, `va_index`, return value, and function arguments. 432 template<typename... Args> 433 ASMJIT_INLINE_CONSTEXPR FuncSignature(CallConvId call_conv_id, uint32_t va_index, TypeId ret, Args&&...args) noexcept 434 : _call_conv_id(call_conv_id), 435 _arg_count(uint8_t(sizeof...(args))), 436 _va_index(uint8_t(va_index)), 437 _ret(ret), 438 _args{std::forward<Args>(args)...} {} 439 440 //! Builds a function signature based on `RetValueAndArgs`. The first template argument is a function return type, 441 //! and function arguments follow. 442 //! 443 //! \note This function returns a new function signature, which can be passed to functions where it's required. It's 444 //! a convenience function that allows to build function signature statically based on types known at compile time, 445 //! which is common in JIT code generation. 446 template<typename... RetValueAndArgs> 447 [[nodiscard]] 448 static ASMJIT_INLINE_CONSTEXPR FuncSignature build(CallConvId call_conv_id = CallConvId::kCDecl, uint32_t va_index = kNoVarArgs) noexcept { 449 return FuncSignature(call_conv_id, va_index, (TypeId(TypeUtils::TypeIdOfT<RetValueAndArgs>::kTypeId))... ); 450 } 451 452 //! \} 453 454 //! \name Overloaded Operators 455 //! \{ 456 457 //! Copy assignment - function signature can be copied by value. 458 ASMJIT_INLINE FuncSignature& operator=(const FuncSignature& other) noexcept = default; 459 460 //! Compares this function signature with `other` for equality.. 461 [[nodiscard]] 462 ASMJIT_INLINE bool operator==(const FuncSignature& other) const noexcept { return equals(other); } 463 464 //! Compares this function signature with `other` for inequality.. 465 [[nodiscard]] 466 ASMJIT_INLINE bool operator!=(const FuncSignature& other) const noexcept { return !equals(other); } 467 468 //! \} 469 470 //! \name Initialization & Reset 471 //! \{ 472 473 //! Resets this function signature to a default constructed state. 474 ASMJIT_INLINE_NODEBUG void reset() noexcept { *this = FuncSignature{}; } 475 476 //! \} 477 478 //! \name Equality & Comparison 479 //! \{ 480 481 //! Compares this function signature with `other` for equality.. 482 [[nodiscard]] 483 ASMJIT_INLINE_NODEBUG bool equals(const FuncSignature& other) const noexcept { 484 return _call_conv_id == other._call_conv_id && 485 _arg_count == other._arg_count && 486 _va_index == other._va_index && 487 _ret == other._ret && 488 memcmp(_args, other._args, sizeof(_args)) == 0; 489 } 490 491 //! \} 492 493 //! \name Accessors 494 //! \{ 495 496 //! Returns the calling convention. 497 [[nodiscard]] 498 ASMJIT_INLINE_CONSTEXPR CallConvId call_conv_id() const noexcept { return _call_conv_id; } 499 500 //! Sets the calling convention to `call_conv_id`; 501 ASMJIT_INLINE_CONSTEXPR void set_call_conv_id(CallConvId call_conv_id) noexcept { _call_conv_id = call_conv_id; } 502 503 //! Tests whether the function signature has a return value. 504 [[nodiscard]] 505 ASMJIT_INLINE_CONSTEXPR bool has_ret() const noexcept { return _ret != TypeId::kVoid; } 506 507 //! Returns the type of the return value. 508 [[nodiscard]] 509 ASMJIT_INLINE_CONSTEXPR TypeId ret() const noexcept { return _ret; } 510 511 //! Sets the return type to `ret_type`. 512 ASMJIT_INLINE_CONSTEXPR void set_ret(TypeId ret_type) noexcept { _ret = ret_type; } 513 514 //! Sets the return type based on `T`. 515 template<typename T> 516 ASMJIT_INLINE_CONSTEXPR void set_ret_t() noexcept { set_ret(TypeId(TypeUtils::TypeIdOfT<T>::kTypeId)); } 517 518 //! Returns the array of function arguments' types. 519 [[nodiscard]] 520 ASMJIT_INLINE_CONSTEXPR const TypeId* args() const noexcept { return _args; } 521 522 //! Returns the number of function arguments. 523 [[nodiscard]] 524 ASMJIT_INLINE_CONSTEXPR uint32_t arg_count() const noexcept { return _arg_count; } 525 526 //! Returns the type of the argument at index `i`. 527 [[nodiscard]] 528 ASMJIT_INLINE TypeId arg(uint32_t i) const noexcept { 529 ASMJIT_ASSERT(i < _arg_count); 530 return _args[i]; 531 } 532 533 //! Sets the argument at index `index` to `arg_type`. 534 ASMJIT_INLINE void set_arg(uint32_t index, TypeId arg_type) noexcept { 535 ASMJIT_ASSERT(index < _arg_count); 536 _args[index] = arg_type; 537 } 538 539 //! Sets the argument at index `i` to the type based on `T`. 540 template<typename T> 541 ASMJIT_INLINE void set_arg_t(uint32_t index) noexcept { set_arg(index, TypeId(TypeUtils::TypeIdOfT<T>::kTypeId)); } 542 543 //! Tests whether an argument can be added to the signature, use before calling \ref add_arg() and \ref add_arg_t(). 544 //! 545 //! \note If you know that you are not adding more arguments than \ref Globals::kMaxFuncArgs then it's not necessary 546 //! to use this function. However, if you are adding arguments based on user input, for example, then either check 547 //! the number of arguments before using function signature or use \ref can_add_arg() before actually adding them to 548 //! the function signature. 549 [[nodiscard]] 550 ASMJIT_INLINE bool can_add_arg() const noexcept { return _arg_count < Globals::kMaxFuncArgs; } 551 552 //! Appends an argument of `type` to the function prototype. 553 ASMJIT_INLINE void add_arg(TypeId type) noexcept { 554 ASMJIT_ASSERT(_arg_count < Globals::kMaxFuncArgs); 555 _args[_arg_count++] = type; 556 } 557 558 //! Appends an argument of type based on `T` to the function prototype. 559 template<typename T> 560 ASMJIT_INLINE void add_arg_t() noexcept { add_arg(TypeId(TypeUtils::TypeIdOfT<T>::kTypeId)); } 561 562 //! Tests whether the function has variable number of arguments (...). 563 [[nodiscard]] 564 ASMJIT_INLINE_NODEBUG bool has_var_args() const noexcept { return _va_index != kNoVarArgs; } 565 566 //! Returns the variable arguments (...) index, `kNoVarArgs` if none. 567 [[nodiscard]] 568 ASMJIT_INLINE_NODEBUG uint32_t va_index() const noexcept { return _va_index; } 569 570 //! Sets the variable arguments (...) index to `index`. 571 ASMJIT_INLINE_NODEBUG void set_va_index(uint32_t index) noexcept { _va_index = uint8_t(index); } 572 573 //! Resets the variable arguments index (making it a non-va function). 574 ASMJIT_INLINE_NODEBUG void reset_va_index() noexcept { _va_index = kNoVarArgs; } 575 576 //! \} 577 }; 578 579 //! Argument or return value (or its part) as defined by `FuncSignature`, but with register or stack address 580 //! (and other metadata) assigned. 581 struct FuncValue { 582 //! \name Constants 583 //! \{ 584 585 enum Bits : uint32_t { 586 kTypeIdShift = 0, //!< TypeId shift. 587 kTypeIdMask = 0x000000FFu, //!< TypeId mask. 588 589 kFlagIsReg = 0x00000100u, //!< Passed by register. 590 kFlagIsStack = 0x00000200u, //!< Passed by stack. 591 kFlagIsIndirect = 0x00000400u, //!< Passed indirectly by reference (internally a pointer). 592 kFlagIsDone = 0x00000800u, //!< Used internally by arguments allocator. 593 594 kStackOffsetShift = 12, //!< Stack offset shift. 595 kStackOffsetMask = 0xFFFFF000u, //!< Stack offset mask (must occupy MSB bits). 596 597 kRegIdShift = 16, //!< RegId shift. 598 kRegIdMask = 0x00FF0000u, //!< RegId mask. 599 600 kRegTypeShift = 24, //!< RegType shift. 601 kRegTypeMask = 0xFF000000u //!< RegType mask. 602 }; 603 604 //! \} 605 606 //! \name Members 607 //! \{ 608 609 uint32_t _data; 610 611 //! \} 612 613 //! \name Initialization & Reset 614 //! 615 //! These initialize the whole `FuncValue` to either register or stack. Useful when you know all of these 616 //! properties and wanna just set it up. 617 //! 618 //! \{ 619 620 //! Initializes this `FuncValue` only to the `type_id` provided - the rest of the values will be cleared. 621 ASMJIT_INLINE_NODEBUG void init_type_id(TypeId type_id) noexcept { 622 _data = uint32_t(type_id) << kTypeIdShift; 623 } 624 625 //! Initializes this `FuncValue` to a register of `reg_type`, `reg_id`, and assigns its `type_id` and `flags`. 626 ASMJIT_INLINE_NODEBUG void init_reg(RegType reg_type, uint32_t reg_id, TypeId type_id, uint32_t flags = 0) noexcept { 627 _data = (uint32_t(reg_type) << kRegTypeShift) | (reg_id << kRegIdShift) | (uint32_t(type_id) << kTypeIdShift) | kFlagIsReg | flags; 628 } 629 630 //! Initializes this `FuncValue` to a stack at the given `offset` and assigns its `type_id`. 631 ASMJIT_INLINE_NODEBUG void init_stack(int32_t offset, TypeId type_id) noexcept { 632 _data = (uint32_t(offset) << kStackOffsetShift) | (uint32_t(type_id) << kTypeIdShift) | kFlagIsStack; 633 } 634 635 //! Resets the value to its unassigned state. 636 ASMJIT_INLINE_NODEBUG void reset() noexcept { _data = 0; } 637 638 //! \} 639 640 //! \name Assign 641 //! 642 //! These initialize only part of `FuncValue`, useful when building `FuncValue` incrementally. The caller 643 //! should first init the type-id by calling `init_type_id` and then continue building either register or stack. 644 //! 645 //! \{ 646 647 //! Assigns a register of `reg_type` and `reg_id`. 648 ASMJIT_INLINE void assign_reg_data(RegType reg_type, uint32_t reg_id) noexcept { 649 ASMJIT_ASSERT((_data & (kRegTypeMask | kRegIdMask)) == 0); 650 _data |= (uint32_t(reg_type) << kRegTypeShift) | (reg_id << kRegIdShift) | kFlagIsReg; 651 } 652 653 //! Assigns a stack location at `offset`. 654 ASMJIT_INLINE void assign_stack_offset(int32_t offset) noexcept { 655 ASMJIT_ASSERT((_data & kStackOffsetMask) == 0); 656 _data |= (uint32_t(offset) << kStackOffsetShift) | kFlagIsStack; 657 } 658 659 //! \} 660 661 //! \name Accessors 662 //! \{ 663 664 //! Returns true if the value is initialized (explicit bool cast). 665 ASMJIT_INLINE_NODEBUG explicit operator bool() const noexcept { return _data != 0; } 666 667 //! \cond INTERNAL 668 ASMJIT_INLINE_NODEBUG void _replace_value(uint32_t mask, uint32_t value) noexcept { _data = (_data & ~mask) | value; } 669 //! \endcond 670 671 //! Tests whether the `FuncValue` has a flag `flag` set. 672 [[nodiscard]] 673 ASMJIT_INLINE_NODEBUG bool has_flag(uint32_t flag) const noexcept { return Support::test(_data, flag); } 674 675 //! Adds `flags` to `FuncValue`. 676 ASMJIT_INLINE_NODEBUG void add_flags(uint32_t flags) noexcept { _data |= flags; } 677 678 //! Clears `flags` of `FuncValue`. 679 ASMJIT_INLINE_NODEBUG void clear_flags(uint32_t flags) noexcept { _data &= ~flags; } 680 681 //! Tests whether the value is initialized (i.e. contains a valid data). 682 [[nodiscard]] 683 ASMJIT_INLINE_NODEBUG bool is_initialized() const noexcept { return _data != 0; } 684 685 //! Tests whether the argument is passed by register. 686 [[nodiscard]] 687 ASMJIT_INLINE_NODEBUG bool is_reg() const noexcept { return has_flag(kFlagIsReg); } 688 689 //! Tests whether the argument is passed by stack. 690 [[nodiscard]] 691 ASMJIT_INLINE_NODEBUG bool is_stack() const noexcept { return has_flag(kFlagIsStack); } 692 693 //! Tests whether the argument is passed by register. 694 [[nodiscard]] 695 ASMJIT_INLINE_NODEBUG bool is_assigned() const noexcept { return has_flag(kFlagIsReg | kFlagIsStack); } 696 697 //! Tests whether the argument is passed through a pointer (used by WIN64 to pass XMM|YMM|ZMM). 698 [[nodiscard]] 699 ASMJIT_INLINE_NODEBUG bool is_indirect() const noexcept { return has_flag(kFlagIsIndirect); } 700 701 //! Tests whether the argument was already processed (used internally). 702 [[nodiscard]] 703 ASMJIT_INLINE_NODEBUG bool is_done() const noexcept { return has_flag(kFlagIsDone); } 704 705 //! Returns a register type of the register used to pass function argument or return value. 706 [[nodiscard]] 707 ASMJIT_INLINE_NODEBUG RegType reg_type() const noexcept { return RegType((_data & kRegTypeMask) >> kRegTypeShift); } 708 709 //! Sets a register type of the register used to pass function argument or return value. 710 ASMJIT_INLINE_NODEBUG void set_reg_type(RegType reg_type) noexcept { _replace_value(kRegTypeMask, uint32_t(reg_type) << kRegTypeShift); } 711 712 //! Returns a physical id of the register used to pass function argument or return value. 713 [[nodiscard]] 714 ASMJIT_INLINE_NODEBUG uint32_t reg_id() const noexcept { return (_data & kRegIdMask) >> kRegIdShift; } 715 716 //! Sets a physical id of the register used to pass function argument or return value. 717 ASMJIT_INLINE_NODEBUG void set_reg_id(uint32_t reg_id) noexcept { _replace_value(kRegIdMask, reg_id << kRegIdShift); } 718 719 //! Returns a stack offset of this argument. 720 [[nodiscard]] 721 ASMJIT_INLINE_NODEBUG int32_t stack_offset() const noexcept { return int32_t(_data & kStackOffsetMask) >> kStackOffsetShift; } 722 723 //! Sets a stack offset of this argument. 724 ASMJIT_INLINE_NODEBUG void set_stack_offset(int32_t offset) noexcept { _replace_value(kStackOffsetMask, uint32_t(offset) << kStackOffsetShift); } 725 726 //! Tests whether the argument or return value has associated `TypeId`. 727 [[nodiscard]] 728 ASMJIT_INLINE_NODEBUG bool has_type_id() const noexcept { return Support::test(_data, kTypeIdMask); } 729 730 //! Returns a TypeId of this argument or return value. 731 [[nodiscard]] 732 ASMJIT_INLINE_NODEBUG TypeId type_id() const noexcept { return TypeId((_data & kTypeIdMask) >> kTypeIdShift); } 733 734 //! Sets a TypeId of this argument or return value. 735 ASMJIT_INLINE_NODEBUG void set_type_id(TypeId type_id) noexcept { _replace_value(kTypeIdMask, uint32_t(type_id) << kTypeIdShift); } 736 737 //! \} 738 }; 739 740 //! Contains multiple `FuncValue` instances in an array so functions that use multiple registers for arguments or 741 //! return values can represent all inputs and outputs. 742 struct FuncValuePack { 743 public: 744 //! \name Members 745 //! \{ 746 747 //! Values of the pack. 748 FuncValue _values[Globals::kMaxValuePack]; 749 750 //! \} 751 752 //! \name Initialization & Reset 753 //! \{ 754 755 //! Resets all values in the pack. 756 ASMJIT_INLINE void reset() noexcept { 757 for (FuncValue& value : _values) { 758 value.reset(); 759 } 760 } 761 762 //! \} 763 764 //! \name Accessors 765 //! \{ 766 767 //! Calculates how many values are in the pack, checking for non-values from the end. 768 [[nodiscard]] 769 ASMJIT_INLINE uint32_t count() const noexcept { 770 uint32_t n = Globals::kMaxValuePack; 771 while (n && !_values[n - 1]) 772 n--; 773 return n; 774 } 775 776 //! Returns values in this value in the pack. 777 //! 778 //! \note The returned array has exactly \ref Globals::kMaxValuePack elements. 779 [[nodiscard]] 780 ASMJIT_INLINE_NODEBUG FuncValue* values() noexcept { return _values; } 781 782 //! \overload 783 [[nodiscard]] 784 ASMJIT_INLINE_NODEBUG const FuncValue* values() const noexcept { return _values; } 785 786 //! Resets a value at the given `index` in the pack, which makes it unassigned. 787 ASMJIT_INLINE void reset_value(size_t index) noexcept { 788 ASMJIT_ASSERT(index < Globals::kMaxValuePack); 789 _values[index].reset(); 790 } 791 792 //! Tests whether the value at the given `index` in the pack is assigned. 793 ASMJIT_INLINE bool has_value(size_t index) noexcept { 794 ASMJIT_ASSERT(index < Globals::kMaxValuePack); 795 return _values[index].is_initialized(); 796 } 797 798 //! Assigns a register at the given `index` to `reg` and an optional `type_id`. 799 ASMJIT_INLINE void assign_reg(size_t index, const Reg& reg, TypeId type_id = TypeId::kVoid) noexcept { 800 ASMJIT_ASSERT(index < Globals::kMaxValuePack); 801 ASMJIT_ASSERT(reg.is_phys_reg()); 802 _values[index].init_reg(reg.reg_type(), reg.id(), type_id); 803 } 804 805 //! Assigns a register at the given `index` to `reg_type`, `reg_id`, and an optional `type_id`. 806 ASMJIT_INLINE void assign_reg(size_t index, RegType reg_type, uint32_t reg_id, TypeId type_id = TypeId::kVoid) noexcept { 807 ASMJIT_ASSERT(index < Globals::kMaxValuePack); 808 _values[index].init_reg(reg_type, reg_id, type_id); 809 } 810 811 //! Assigns a stack location at the given `index` to `offset` and an optional `type_id`. 812 ASMJIT_INLINE void assign_stack(size_t index, int32_t offset, TypeId type_id = TypeId::kVoid) noexcept { 813 ASMJIT_ASSERT(index < Globals::kMaxValuePack); 814 _values[index].init_stack(offset, type_id); 815 } 816 817 //! Accesses the value in the pack at the given `index`. 818 //! 819 //! \note The maximum index value is `Globals::kMaxValuePack - 1`. 820 [[nodiscard]] 821 ASMJIT_INLINE FuncValue& operator[](size_t index) { 822 ASMJIT_ASSERT(index < Globals::kMaxValuePack); 823 return _values[index]; 824 } 825 826 //! \overload 827 [[nodiscard]] 828 ASMJIT_INLINE const FuncValue& operator[](size_t index) const { 829 ASMJIT_ASSERT(index < Globals::kMaxValuePack); 830 return _values[index]; 831 } 832 833 //! \} 834 }; 835 836 //! Attributes are designed in a way that all are initially false, and user or \ref FuncFrame finalizer adds 837 //! them when necessary. 838 enum class FuncAttributes : uint32_t { 839 //! No attributes. 840 kNoAttributes = 0, 841 842 //! Function has variable number of arguments. 843 kHasVarArgs = 0x00000001u, 844 //! Preserve frame pointer (don't omit FP). 845 kHasPreservedFP = 0x00000010u, 846 //! Function calls other functions (is not leaf). 847 kHasFuncCalls = 0x00000020u, 848 //! Function has aligned save/restore of vector registers. 849 kAlignedVecSR = 0x00000040u, 850 //! Function must begin with an instruction that marks a start of a branch or function. 851 //! 852 //! - `ENDBR32/ENDBR64` instruction is inserted at the beginning of the function (X86|X86_64). 853 //! - `BTI` instruction is inserted at the beginning of the function (AArch64). 854 kIndirectBranchProtection = 0x00000080u, 855 //! FuncFrame is finalized and can be used by prolog/epilog inserter (PEI). 856 kIsFinalized = 0x00000800u, 857 858 // X86 Specific Attributes 859 // ----------------------- 860 861 //! Enables the use of AVX within the function's body, prolog, and epilog (X86|X86_64). 862 //! 863 //! This flag instructs prolog and epilog emitter to use AVX instead of SSE for manipulating XMM registers. 864 kX86_AVXEnabled = 0x00010000u, 865 866 //! Enables the use of AVX-512 within the function's body, prolog, and epilog (X86|X86_64). 867 //! 868 //! This flag instructs Compiler register allocator to use additional 16 registers introduced by AVX-512. 869 //! Additionally, if the functions saves full width of ZMM registers (custom calling conventions only) then 870 //! the prolog/epilog inserter would use AVX-512 move instructions to emit the save and restore sequence. 871 kX86_AVX512Enabled = 0x00020000u, 872 873 //! This flag instructs the epilog writer to emit EMMS instruction before RET (X86|X86_64). 874 kX86_MMXCleanup = 0x00040000u, 875 876 //! This flag instructs the epilog writer to emit VZEROUPPER instruction before RET (X86|X86_64). 877 kX86_AVXCleanup = 0x00080000u, 878 879 //! This flag instructs the epilog writer to emit VZEROUPPER only if there are dirty vector registers (X86|X86_64). 880 kX86_AVXAutoCleanup = 0x00100000u 881 }; 882 ASMJIT_DEFINE_ENUM_FLAGS(FuncAttributes) 883 884 //! Function detail - \ref CallConv and expanded \ref FuncSignature. 885 //! 886 //! Function detail is architecture and OS dependent representation of a function. It contains a materialized 887 //! calling convention and expanded function signature so all arguments have assigned either register type/id 888 //! or stack address. 889 class FuncDetail { 890 public: 891 //! \name Constants 892 //! \{ 893 894 //! Function doesn't have a variable number of arguments (`...`). 895 static inline constexpr uint8_t kNoVarArgs = 0xFFu; 896 897 //! \} 898 899 //! \name Members 900 //! \{ 901 902 //! Calling convention. 903 CallConv _call_conv {}; 904 //! Number of function arguments. 905 uint8_t _arg_count = 0; 906 //! Variable arguments index of `kNoVarArgs`. 907 uint8_t _va_index = 0; 908 //! Reserved for future use. 909 uint16_t _reserved = 0; 910 //! Registers that contain arguments. 911 Support::Array<RegMask, Globals::kNumVirtGroups> _used_regs {}; 912 //! Size of arguments passed by stack. 913 uint32_t _arg_stack_size = 0; 914 //! Function return value(s). 915 FuncValuePack _rets {}; 916 //! Function arguments. 917 FuncValuePack _args[Globals::kMaxFuncArgs] {}; 918 919 //! \} 920 921 //! \name Construction & Destruction 922 //! \{ 923 924 //! Creates a default constructed \ref FuncDetail. 925 ASMJIT_INLINE_NODEBUG FuncDetail() noexcept {} 926 927 //! Copy constructor. 928 //! 929 //! Function details are copyable. 930 ASMJIT_INLINE_NODEBUG FuncDetail(const FuncDetail& other) noexcept = default; 931 932 //! Initializes this `FuncDetail` to the given signature. 933 ASMJIT_API Error init(const FuncSignature& signature, const Environment& environment) noexcept; 934 935 //! \} 936 937 //! \name Overloaded Operators 938 //! \{ 939 940 //! Assignment operator, copies `other` to this \ref FuncDetail. 941 ASMJIT_INLINE_NODEBUG FuncDetail& operator=(const FuncDetail& other) noexcept = default; 942 943 //! \} 944 945 //! \name Reset 946 //! \{ 947 948 //! Resets the function detail to its default constructed state. 949 ASMJIT_INLINE_NODEBUG void reset() noexcept { *this = FuncDetail{}; } 950 951 //! \} 952 953 //! \name Accessors 954 //! \{ 955 956 //! Returns the function's calling convention, see `CallConv`. 957 [[nodiscard]] 958 ASMJIT_INLINE_NODEBUG const CallConv& call_conv() const noexcept { return _call_conv; } 959 960 //! Returns the associated calling convention flags, see `CallConv::Flags`. 961 [[nodiscard]] 962 ASMJIT_INLINE_NODEBUG CallConvFlags flags() const noexcept { return _call_conv.flags(); } 963 964 //! Checks whether a CallConv `flag` is set, see `CallConv::Flags`. 965 [[nodiscard]] 966 ASMJIT_INLINE_NODEBUG bool has_flag(CallConvFlags flag) const noexcept { return _call_conv.has_flag(flag); } 967 968 //! Tests whether the function has a return value. 969 [[nodiscard]] 970 ASMJIT_INLINE_NODEBUG bool has_ret() const noexcept { return bool(_rets[0]); } 971 972 //! Returns the number of function arguments. 973 [[nodiscard]] 974 ASMJIT_INLINE_NODEBUG uint32_t arg_count() const noexcept { return _arg_count; } 975 976 //! Returns function return values. 977 [[nodiscard]] 978 ASMJIT_INLINE_NODEBUG FuncValuePack& ret_pack() noexcept { return _rets; } 979 980 //! Returns function return values. 981 [[nodiscard]] 982 ASMJIT_INLINE_NODEBUG const FuncValuePack& ret_pack() const noexcept { return _rets; } 983 984 //! Returns a function return value associated with the given `value_index`. 985 [[nodiscard]] 986 ASMJIT_INLINE_NODEBUG FuncValue& ret(size_t value_index = 0) noexcept { return _rets[value_index]; } 987 988 //! Returns a function return value associated with the given `value_index` (const). 989 [[nodiscard]] 990 ASMJIT_INLINE_NODEBUG const FuncValue& ret(size_t value_index = 0) const noexcept { return _rets[value_index]; } 991 992 //! Returns function argument packs array. 993 [[nodiscard]] 994 ASMJIT_INLINE_NODEBUG FuncValuePack* arg_packs() noexcept { return _args; } 995 996 //! Returns function argument packs array (const). 997 [[nodiscard]] 998 ASMJIT_INLINE_NODEBUG const FuncValuePack* arg_packs() const noexcept { return _args; } 999 1000 //! Returns function argument pack at the given `arg_index`. 1001 [[nodiscard]] 1002 ASMJIT_INLINE FuncValuePack& arg_pack(size_t arg_index) noexcept { 1003 ASMJIT_ASSERT(arg_index < Globals::kMaxFuncArgs); 1004 return _args[arg_index]; 1005 } 1006 1007 //! Returns function argument pack at the given `arg_index` (const). 1008 [[nodiscard]] 1009 ASMJIT_INLINE const FuncValuePack& arg_pack(size_t arg_index) const noexcept { 1010 ASMJIT_ASSERT(arg_index < Globals::kMaxFuncArgs); 1011 return _args[arg_index]; 1012 } 1013 1014 //! Returns an argument at `value_index` from the argument pack at the given `arg_index`. 1015 [[nodiscard]] 1016 ASMJIT_INLINE FuncValue& arg(size_t arg_index, size_t value_index = 0) noexcept { 1017 ASMJIT_ASSERT(arg_index < Globals::kMaxFuncArgs); 1018 return _args[arg_index][value_index]; 1019 } 1020 1021 //! Returns an argument at `value_index` from the argument pack at the given `arg_index` (const). 1022 [[nodiscard]] 1023 ASMJIT_INLINE const FuncValue& arg(size_t arg_index, size_t value_index = 0) const noexcept { 1024 ASMJIT_ASSERT(arg_index < Globals::kMaxFuncArgs); 1025 return _args[arg_index][value_index]; 1026 } 1027 1028 //! Resets an argument at the given `arg_index`. 1029 //! 1030 //! If the argument is a parameter pack (has multiple values) all values are reset. 1031 ASMJIT_INLINE void reset_arg(size_t arg_index) noexcept { 1032 ASMJIT_ASSERT(arg_index < Globals::kMaxFuncArgs); 1033 _args[arg_index].reset(); 1034 } 1035 1036 //! Tests whether the function has variable arguments. 1037 [[nodiscard]] 1038 ASMJIT_INLINE_NODEBUG bool has_var_args() const noexcept { return _va_index != kNoVarArgs; } 1039 1040 //! Returns an index of a first variable argument. 1041 [[nodiscard]] 1042 ASMJIT_INLINE_NODEBUG uint32_t va_index() const noexcept { return _va_index; } 1043 1044 //! Tests whether the function passes one or more argument by stack. 1045 [[nodiscard]] 1046 ASMJIT_INLINE_NODEBUG bool has_stack_args() const noexcept { return _arg_stack_size != 0; } 1047 1048 //! Returns stack size needed for function arguments passed on the stack. 1049 [[nodiscard]] 1050 ASMJIT_INLINE_NODEBUG uint32_t arg_stack_size() const noexcept { return _arg_stack_size; } 1051 1052 //! Returns red zone size. 1053 [[nodiscard]] 1054 ASMJIT_INLINE_NODEBUG uint32_t red_zone_size() const noexcept { return _call_conv.red_zone_size(); } 1055 1056 //! Returns spill zone size. 1057 [[nodiscard]] 1058 ASMJIT_INLINE_NODEBUG uint32_t spill_zone_size() const noexcept { return _call_conv.spill_zone_size(); } 1059 1060 //! Returns natural stack alignment. 1061 [[nodiscard]] 1062 ASMJIT_INLINE_NODEBUG uint32_t natural_stack_alignment() const noexcept { return _call_conv.natural_stack_alignment(); } 1063 1064 //! Returns a mask of all passed registers of the given register `group`. 1065 [[nodiscard]] 1066 ASMJIT_INLINE_NODEBUG RegMask passed_regs(RegGroup group) const noexcept { return _call_conv.passed_regs(group); } 1067 1068 //! Returns a mask of all preserved registers of the given register `group`. 1069 [[nodiscard]] 1070 ASMJIT_INLINE_NODEBUG RegMask preserved_regs(RegGroup group) const noexcept { return _call_conv.preserved_regs(group); } 1071 1072 //! Returns a mask of all used registers of the given register `group`. 1073 [[nodiscard]] 1074 ASMJIT_INLINE RegMask used_regs(RegGroup group) const noexcept { 1075 ASMJIT_ASSERT(group <= RegGroup::kMaxVirt); 1076 return _used_regs[size_t(group)]; 1077 } 1078 1079 //! Adds `regs` to the mask of used registers of the given register `group`. 1080 ASMJIT_INLINE void add_used_regs(RegGroup group, RegMask regs) noexcept { 1081 ASMJIT_ASSERT(group <= RegGroup::kMaxVirt); 1082 _used_regs[size_t(group)] |= regs; 1083 } 1084 1085 //! \} 1086 }; 1087 1088 //! Function frame. 1089 //! 1090 //! Function frame is used directly by prolog and epilog insertion (PEI) utils. It provides information necessary to 1091 //! insert a proper and ABI conforming prolog and epilog. Function frame calculation is based on `CallConv` and 1092 //! other function attributes. 1093 //! 1094 //! SSE vs AVX vs AVX-512 1095 //! --------------------- 1096 //! 1097 //! Function frame provides a way to tell prolog/epilog inserter to use AVX instructions instead of SSE. Use 1098 //! `set_avx_enabled()` and `set_avx512_enabled()` to enable AVX and/or AVX-512, respectively. Enabling AVX-512 1099 //! is mostly for Compiler as it would use 32 SIMD registers instead of 16 when enabled. 1100 //! 1101 //! \note If your code uses AVX instructions and AVX is not enabled there would be a performance hit in case that 1102 //! some registers had to be saved/restored in function's prolog/epilog, respectively. Thus, it's recommended to 1103 //! always let the function frame know about the use of AVX. 1104 //! 1105 //! Function Frame Structure 1106 //! ------------------------ 1107 //! 1108 //! Various properties can contribute to the size and structure of the function frame. The function frame in most 1109 //! cases won't use all of the properties illustrated (for example Spill Zone and Red Zone are never used together). 1110 //! 1111 //! ``` 1112 //! +-----------------------------+ 1113 //! | Arguments Passed by Stack | 1114 //! +-----------------------------+ 1115 //! | Spill Zone | 1116 //! +-----------------------------+ <- Stack offset (args) starts from here. 1117 //! | Return Address, if Pushed | 1118 //! +-----------------------------+ <- Stack pointer (SP) upon entry. 1119 //! | Save/Restore Stack. | 1120 //! +-----------------------------+-----------------------------+ 1121 //! | Local Stack | | 1122 //! +-----------------------------+ Final Stack | 1123 //! | Call Stack | | 1124 //! +-----------------------------+-----------------------------+ <- SP after prolog. 1125 //! | Red Zone | 1126 //! +-----------------------------+ 1127 //! ``` 1128 class FuncFrame { 1129 public: 1130 //! \name Constants 1131 //! \{ 1132 1133 //! Tag used to inform that some offset is invalid. 1134 static inline constexpr uint32_t kTagInvalidOffset = 0xFFFFFFFFu; 1135 1136 //! \} 1137 1138 //! \name Types 1139 //! \{ 1140 1141 using RegMasks = Support::Array<RegMask, Globals::kNumVirtGroups>; 1142 1143 //! \} 1144 1145 //! \name Members 1146 //! \{ 1147 1148 //! Function attributes. 1149 FuncAttributes _attributes {}; 1150 1151 //! Target architecture. 1152 Arch _arch {}; 1153 //! SP register ID (to access call stack and local stack). 1154 uint8_t _sp_reg_id = uint8_t(Reg::kIdBad); 1155 //! SA register ID (to access stack arguments). 1156 uint8_t _sa_reg_id = uint8_t(Reg::kIdBad); 1157 1158 //! Red zone size (copied from CallConv). 1159 uint8_t _red_zone_size = 0; 1160 //! Spill zone size (copied from CallConv). 1161 uint8_t _spill_zone_size = 0; 1162 //! Natural stack alignment (copied from CallConv). 1163 uint8_t _natural_stack_alignment = 0; 1164 //! Minimum stack alignment to turn on dynamic alignment. 1165 uint8_t _min_dynamic_alignment = 0; 1166 1167 //! Call stack alignment. 1168 uint8_t _call_stack_alignment = 0; 1169 //! Local stack alignment. 1170 uint8_t _local_stack_alignment = 0; 1171 //! Final stack alignment. 1172 uint8_t _final_stack_alignment = 0; 1173 1174 //! Adjustment of the stack before returning (X86-STDCALL). 1175 uint16_t _callee_stack_cleanup = 0; 1176 1177 //! Call stack size. 1178 uint32_t _call_stack_size = 0; 1179 //! Local stack size. 1180 uint32_t _local_stack_size = 0; 1181 //! Final stack size (sum of call stack and local stack). 1182 uint32_t _final_stack_size = 0; 1183 1184 //! Local stack offset (non-zero only if call stack is used). 1185 uint32_t _local_stack_offset = 0; 1186 //! Offset relative to SP that contains previous SP (before alignment). 1187 uint32_t _da_offset = 0; 1188 //! Offset of the first stack argument relative to SP. 1189 uint32_t _sa_offset_from_sp = 0; 1190 //! Offset of the first stack argument relative to SA (_sa_reg_id or FP). 1191 uint32_t _sa_offset_from_sa = 0; 1192 1193 //! Local stack adjustment in prolog/epilog. 1194 uint32_t _stack_adjustment = 0; 1195 1196 //! Registers that are dirty. 1197 RegMasks _dirty_regs {}; 1198 //! Registers that must be preserved (copied from CallConv). 1199 RegMasks _preserved_regs {}; 1200 //! Registers that are unavailable. 1201 RegMasks _unavailable_regs {}; 1202 //! Size to save/restore per register group. 1203 Support::Array<uint8_t, Globals::kNumVirtGroups> _save_restore_reg_size {}; 1204 //! Alignment of save/restore area per register group. 1205 Support::Array<uint8_t, Globals::kNumVirtGroups> _save_restore_alignment {}; 1206 1207 //! Stack size required to save registers with push/pop. 1208 uint16_t _push_pop_save_size = 0; 1209 //! Stack size required to save extra registers that cannot use push/pop. 1210 uint16_t _extra_reg_save_size = 0; 1211 //! Offset where registers saved/restored via push/pop are stored 1212 uint32_t _push_pop_save_offset = 0; 1213 //! Offset where extra registers that cannot use push/pop are stored. 1214 uint32_t _extra_reg_save_offset = 0; 1215 1216 //! \} 1217 1218 //! \name Construction & Destruction 1219 //! \{ 1220 1221 //! Creates a default constructed function frame, which has initialized all members to their default values. 1222 ASMJIT_INLINE_NODEBUG FuncFrame() noexcept = default; 1223 //! Creates a copy of `other` function frame. 1224 ASMJIT_INLINE_NODEBUG FuncFrame(const FuncFrame& other) noexcept = default; 1225 1226 //! \} 1227 1228 //! \name Initialization & Reset 1229 //! \{ 1230 1231 //! Initializes the function frame based on `func` detail. 1232 ASMJIT_API Error init(const FuncDetail& func) noexcept; 1233 //! Resets the function frame into its default constructed state. 1234 ASMJIT_INLINE_NODEBUG void reset() noexcept { *this = FuncFrame{}; } 1235 1236 //! \} 1237 1238 //! \name Overloaded Operators 1239 //! \{ 1240 1241 //! Copy assignment - function frame is copy assignable. 1242 ASMJIT_INLINE_NODEBUG FuncFrame& operator=(const FuncFrame& other) noexcept = default; 1243 1244 //! \} 1245 1246 //! \name Accessors 1247 //! \{ 1248 1249 //! Returns the target architecture of the function frame. 1250 [[nodiscard]] 1251 ASMJIT_INLINE_NODEBUG Arch arch() const noexcept { return _arch; } 1252 1253 //! Returns function frame attributes, see `Attributes`. 1254 [[nodiscard]] 1255 ASMJIT_INLINE_NODEBUG FuncAttributes attributes() const noexcept { return _attributes; } 1256 1257 //! Checks whether the FuncFame contains an attribute `attr`. 1258 [[nodiscard]] 1259 ASMJIT_INLINE_NODEBUG bool has_attribute(FuncAttributes attr) const noexcept { return Support::test(_attributes, attr); } 1260 1261 //! Adds attributes `attrs` to the FuncFrame. 1262 ASMJIT_INLINE_NODEBUG void add_attributes(FuncAttributes attrs) noexcept { _attributes |= attrs; } 1263 1264 //! Clears attributes `attrs` from the FrameFrame. 1265 ASMJIT_INLINE_NODEBUG void clear_attributes(FuncAttributes attrs) noexcept { _attributes &= ~attrs; } 1266 1267 //! Tests whether the function has variable number of arguments. 1268 [[nodiscard]] 1269 ASMJIT_INLINE_NODEBUG bool has_var_args() const noexcept { return has_attribute(FuncAttributes::kHasVarArgs); } 1270 1271 //! Sets the variable arguments flag. 1272 ASMJIT_INLINE_NODEBUG void set_var_args() noexcept { add_attributes(FuncAttributes::kHasVarArgs); } 1273 1274 //! Resets variable arguments flag. 1275 ASMJIT_INLINE_NODEBUG void reset_var_args() noexcept { clear_attributes(FuncAttributes::kHasVarArgs); } 1276 1277 //! Tests whether the function preserves frame pointer (EBP|ESP on X86). 1278 [[nodiscard]] 1279 ASMJIT_INLINE_NODEBUG bool has_preserved_fp() const noexcept { return has_attribute(FuncAttributes::kHasPreservedFP); } 1280 1281 //! Enables preserved frame pointer. 1282 ASMJIT_INLINE_NODEBUG void set_preserved_fp() noexcept { add_attributes(FuncAttributes::kHasPreservedFP); } 1283 1284 //! Disables preserved frame pointer. 1285 ASMJIT_INLINE_NODEBUG void reset_preserved_fp() noexcept { clear_attributes(FuncAttributes::kHasPreservedFP); } 1286 1287 //! Tests whether the function calls other functions. 1288 [[nodiscard]] 1289 ASMJIT_INLINE_NODEBUG bool has_func_calls() const noexcept { return has_attribute(FuncAttributes::kHasFuncCalls); } 1290 1291 //! Sets `FuncAttributes::kHasFuncCalls` to true. 1292 ASMJIT_INLINE_NODEBUG void set_func_calls() noexcept { add_attributes(FuncAttributes::kHasFuncCalls); } 1293 1294 //! Sets `FuncAttributes::kHasFuncCalls` to false. 1295 ASMJIT_INLINE_NODEBUG void reset_func_calls() noexcept { clear_attributes(FuncAttributes::kHasFuncCalls); } 1296 1297 //! Tests whether the function uses indirect branch protection, see \ref FuncAttributes::kIndirectBranchProtection. 1298 [[nodiscard]] 1299 ASMJIT_INLINE_NODEBUG bool has_indirect_branch_protection() const noexcept { return has_attribute(FuncAttributes::kIndirectBranchProtection); } 1300 1301 //! Enabled indirect branch protection (sets `FuncAttributes::kIndirectBranchProtection` attribute to true). 1302 ASMJIT_INLINE_NODEBUG void set_indirect_branch_protection() noexcept { add_attributes(FuncAttributes::kIndirectBranchProtection); } 1303 1304 //! Disables indirect branch protection (sets `FuncAttributes::kIndirectBranchProtection` attribute to false). 1305 ASMJIT_INLINE_NODEBUG void reset_indirect_branch_protection() noexcept { clear_attributes(FuncAttributes::kIndirectBranchProtection); } 1306 1307 //! Tests whether the function has AVX enabled. 1308 [[nodiscard]] 1309 ASMJIT_INLINE_NODEBUG bool is_avx_enabled() const noexcept { return has_attribute(FuncAttributes::kX86_AVXEnabled); } 1310 1311 //! Enables AVX use. 1312 ASMJIT_INLINE_NODEBUG void set_avx_enabled() noexcept { add_attributes(FuncAttributes::kX86_AVXEnabled); } 1313 1314 //! Disables AVX use. 1315 ASMJIT_INLINE_NODEBUG void reset_avx_enabled() noexcept { clear_attributes(FuncAttributes::kX86_AVXEnabled); } 1316 1317 //! Tests whether the function has AVX-512 enabled. 1318 [[nodiscard]] 1319 ASMJIT_INLINE_NODEBUG bool is_avx512_enabled() const noexcept { return has_attribute(FuncAttributes::kX86_AVX512Enabled); } 1320 1321 //! Enables AVX-512 use. 1322 ASMJIT_INLINE_NODEBUG void set_avx512_enabled() noexcept { add_attributes(FuncAttributes::kX86_AVX512Enabled); } 1323 1324 //! Disables AVX-512 use. 1325 ASMJIT_INLINE_NODEBUG void reset_avx512_enabled() noexcept { clear_attributes(FuncAttributes::kX86_AVX512Enabled); } 1326 1327 //! Tests whether the function has MMX cleanup - 'emms' instruction in epilog. 1328 [[nodiscard]] 1329 ASMJIT_INLINE_NODEBUG bool has_mmx_cleanup() const noexcept { return has_attribute(FuncAttributes::kX86_MMXCleanup); } 1330 1331 //! Enables MMX cleanup. 1332 ASMJIT_INLINE_NODEBUG void set_mmx_cleanup() noexcept { add_attributes(FuncAttributes::kX86_MMXCleanup); } 1333 1334 //! Disables MMX cleanup. 1335 ASMJIT_INLINE_NODEBUG void reset_mmx_cleanup() noexcept { clear_attributes(FuncAttributes::kX86_MMXCleanup); } 1336 1337 //! Tests whether the function has AVX cleanup - 'vzeroupper' instruction in epilog. 1338 [[nodiscard]] 1339 ASMJIT_INLINE_NODEBUG bool has_avx_cleanup() const noexcept { return has_attribute(FuncAttributes::kX86_AVXCleanup); } 1340 1341 //! Enables AVX cleanup. 1342 ASMJIT_INLINE_NODEBUG void set_avx_cleanup() noexcept { add_attributes(FuncAttributes::kX86_AVXCleanup); } 1343 1344 //! Disables AVX cleanup. 1345 ASMJIT_INLINE_NODEBUG void reset_avx_cleanup() noexcept { clear_attributes(FuncAttributes::kX86_AVXCleanup); } 1346 1347 //! Tests whether the function has automatic AVX cleanup - 'vzeroupper' instruction in epilog when vector registers are 1348 //! used. 1349 //! 1350 //! \note Automatic cleanup is currently determined via dirty registers, which are provided by \ref FuncFrame. 1351 [[nodiscard]] 1352 ASMJIT_INLINE_NODEBUG bool has_avx_auto_cleanup() const noexcept { return has_attribute(FuncAttributes::kX86_AVXAutoCleanup); } 1353 1354 //! Enables AVX automatic cleanup. 1355 ASMJIT_INLINE_NODEBUG void set_avx_auto_cleanup() noexcept { add_attributes(FuncAttributes::kX86_AVXAutoCleanup); } 1356 1357 //! Disables AVX automatic cleanup. 1358 ASMJIT_INLINE_NODEBUG void reset_avx_auto_cleanup() noexcept { clear_attributes(FuncAttributes::kX86_AVXAutoCleanup); } 1359 1360 //! Tests whether the function uses call stack. 1361 [[nodiscard]] 1362 ASMJIT_INLINE_NODEBUG bool has_call_stack() const noexcept { return _call_stack_size != 0; } 1363 1364 //! Tests whether the function uses local stack. 1365 [[nodiscard]] 1366 ASMJIT_INLINE_NODEBUG bool has_local_stack() const noexcept { return _local_stack_size != 0; } 1367 1368 //! Tests whether vector registers can be saved and restored by using aligned reads and writes. 1369 [[nodiscard]] 1370 ASMJIT_INLINE_NODEBUG bool has_aligned_vec_save_restore() const noexcept { return has_attribute(FuncAttributes::kAlignedVecSR); } 1371 1372 //! Tests whether the function has to align stack dynamically. 1373 [[nodiscard]] 1374 ASMJIT_INLINE_NODEBUG bool has_dynamic_alignment() const noexcept { return _final_stack_alignment >= _min_dynamic_alignment; } 1375 1376 //! Tests whether the calling convention specifies 'Red Zone'. 1377 [[nodiscard]] 1378 ASMJIT_INLINE_NODEBUG bool has_red_zone() const noexcept { return _red_zone_size != 0; } 1379 1380 //! Returns the size of 'Red Zone'. 1381 [[nodiscard]] 1382 ASMJIT_INLINE_NODEBUG uint32_t red_zone_size() const noexcept { return _red_zone_size; } 1383 1384 //! Tests whether the calling convention specifies 'Spill Zone'. 1385 [[nodiscard]] 1386 ASMJIT_INLINE_NODEBUG bool has_spill_zone() const noexcept { return _spill_zone_size != 0; } 1387 1388 //! Returns the size of 'Spill Zone'. 1389 [[nodiscard]] 1390 ASMJIT_INLINE_NODEBUG uint32_t spill_zone_size() const noexcept { return _spill_zone_size; } 1391 1392 //! Resets the size of red zone, which would disable it entirely. 1393 //! 1394 //! \note Red zone is currently only used by an AMD64 SystemV calling convention, which expects 128 1395 //! bytes of stack to be accessible below stack pointer. These bytes are then accessible within the 1396 //! function and Compiler can use this space as a spill area. However, sometimes it's better to 1397 //! disallow the use of red zone in case that a user wants to use this stack for a custom purpose. 1398 ASMJIT_INLINE_NODEBUG void reset_red_zone() noexcept { _red_zone_size = 0; } 1399 1400 //! Returns natural stack alignment (guaranteed stack alignment upon entry). 1401 [[nodiscard]] 1402 ASMJIT_INLINE_NODEBUG uint32_t natural_stack_alignment() const noexcept { return _natural_stack_alignment; } 1403 1404 //! Returns natural stack alignment (guaranteed stack alignment upon entry). 1405 [[nodiscard]] 1406 ASMJIT_INLINE_NODEBUG uint32_t min_dynamic_alignment() const noexcept { return _min_dynamic_alignment; } 1407 1408 //! Tests whether the callee must adjust SP before returning (X86-STDCALL only) 1409 [[nodiscard]] 1410 ASMJIT_INLINE_NODEBUG bool has_callee_stack_cleanup() const noexcept { return _callee_stack_cleanup != 0; } 1411 1412 //! Returns home many bytes of the stack the callee must adjust before returning (X86-STDCALL only) 1413 [[nodiscard]] 1414 ASMJIT_INLINE_NODEBUG uint32_t callee_stack_cleanup() const noexcept { return _callee_stack_cleanup; } 1415 1416 //! Returns call stack alignment. 1417 [[nodiscard]] 1418 ASMJIT_INLINE_NODEBUG uint32_t call_stack_alignment() const noexcept { return _call_stack_alignment; } 1419 1420 //! Returns local stack alignment. 1421 [[nodiscard]] 1422 ASMJIT_INLINE_NODEBUG uint32_t local_stack_alignment() const noexcept { return _local_stack_alignment; } 1423 1424 //! Returns final stack alignment (the maximum value of call, local, and natural stack alignments). 1425 [[nodiscard]] 1426 ASMJIT_INLINE_NODEBUG uint32_t final_stack_alignment() const noexcept { return _final_stack_alignment; } 1427 1428 //! Sets call stack alignment. 1429 //! 1430 //! \note This also updates the final stack alignment. 1431 ASMJIT_INLINE void set_call_stack_alignment(uint32_t alignment) noexcept { 1432 _call_stack_alignment = uint8_t(alignment); 1433 _final_stack_alignment = Support::max(_natural_stack_alignment, _call_stack_alignment, _local_stack_alignment); 1434 } 1435 1436 //! Sets local stack alignment. 1437 //! 1438 //! \note This also updates the final stack alignment. 1439 ASMJIT_INLINE void set_local_stack_alignment(uint32_t value) noexcept { 1440 _local_stack_alignment = uint8_t(value); 1441 _final_stack_alignment = Support::max(_natural_stack_alignment, _call_stack_alignment, _local_stack_alignment); 1442 } 1443 1444 //! Combines call stack alignment with `alignment`, updating it to the greater value. 1445 //! 1446 //! \note This also updates the final stack alignment. 1447 ASMJIT_INLINE void update_call_stack_alignment(uint32_t alignment) noexcept { 1448 _call_stack_alignment = uint8_t(Support::max<uint32_t>(_call_stack_alignment, alignment)); 1449 _final_stack_alignment = Support::max(_final_stack_alignment, _call_stack_alignment); 1450 } 1451 1452 //! Combines local stack alignment with `alignment`, updating it to the greater value. 1453 //! 1454 //! \note This also updates the final stack alignment. 1455 ASMJIT_INLINE void update_local_stack_alignment(uint32_t alignment) noexcept { 1456 _local_stack_alignment = uint8_t(Support::max<uint32_t>(_local_stack_alignment, alignment)); 1457 _final_stack_alignment = Support::max(_final_stack_alignment, _local_stack_alignment); 1458 } 1459 1460 //! Returns call stack size. 1461 [[nodiscard]] 1462 ASMJIT_INLINE_NODEBUG uint32_t call_stack_size() const noexcept { return _call_stack_size; } 1463 1464 //! Returns local stack size. 1465 [[nodiscard]] 1466 ASMJIT_INLINE_NODEBUG uint32_t local_stack_size() const noexcept { return _local_stack_size; } 1467 1468 //! Sets call stack size. 1469 ASMJIT_INLINE_NODEBUG void set_call_stack_size(uint32_t size) noexcept { _call_stack_size = size; } 1470 1471 //! Sets local stack size. 1472 ASMJIT_INLINE_NODEBUG void set_local_stack_size(uint32_t size) noexcept { _local_stack_size = size; } 1473 1474 //! Combines call stack size with `size`, updating it to the greater value. 1475 ASMJIT_INLINE_NODEBUG void update_call_stack_size(uint32_t size) noexcept { _call_stack_size = Support::max(_call_stack_size, size); } 1476 1477 //! Combines local stack size with `size`, updating it to the greater value. 1478 ASMJIT_INLINE_NODEBUG void update_local_stack_size(uint32_t size) noexcept { _local_stack_size = Support::max(_local_stack_size, size); } 1479 1480 //! Returns final stack size (only valid after the FuncFrame is finalized). 1481 [[nodiscard]] 1482 ASMJIT_INLINE_NODEBUG uint32_t final_stack_size() const noexcept { return _final_stack_size; } 1483 1484 //! Returns an offset to access the local stack (non-zero only if call stack is used). 1485 [[nodiscard]] 1486 ASMJIT_INLINE_NODEBUG uint32_t local_stack_offset() const noexcept { return _local_stack_offset; } 1487 1488 //! Tests whether the function prolog/epilog requires a memory slot for storing unaligned SP. 1489 [[nodiscard]] 1490 ASMJIT_INLINE_NODEBUG bool has_da_offset() const noexcept { return _da_offset != kTagInvalidOffset; } 1491 1492 //! Returns a memory offset used to store DA (dynamic alignment) slot (relative to SP). 1493 [[nodiscard]] 1494 ASMJIT_INLINE_NODEBUG uint32_t da_offset() const noexcept { return _da_offset; } 1495 1496 [[nodiscard]] 1497 ASMJIT_INLINE_NODEBUG uint32_t sa_offset(uint32_t reg_id) const noexcept { 1498 return reg_id == _sp_reg_id ? sa_offset_from_sp() : sa_offset_from_sa(); 1499 } 1500 1501 [[nodiscard]] 1502 ASMJIT_INLINE_NODEBUG uint32_t sa_offset_from_sp() const noexcept { return _sa_offset_from_sp; } 1503 1504 [[nodiscard]] 1505 ASMJIT_INLINE_NODEBUG uint32_t sa_offset_from_sa() const noexcept { return _sa_offset_from_sa; } 1506 1507 //! Returns mask of registers of the given register `group` that are modified by the function. The engine would 1508 //! then calculate which registers must be saved & restored by the function by using the data provided by the 1509 //! calling convention. 1510 [[nodiscard]] 1511 inline RegMask dirty_regs(RegGroup group) const noexcept { 1512 ASMJIT_ASSERT(group <= RegGroup::kMaxVirt); 1513 return _dirty_regs[group]; 1514 } 1515 1516 //! Sets which registers (as a mask) are modified by the function. 1517 //! 1518 //! \remarks Please note that this will completely overwrite the existing register mask, use `add_dirty_regs()` 1519 //! to modify the existing register mask. 1520 inline void set_dirty_regs(RegGroup group, RegMask regs) noexcept { 1521 ASMJIT_ASSERT(group <= RegGroup::kMaxVirt); 1522 _dirty_regs[group] = regs; 1523 } 1524 1525 //! Adds which registers (as a mask) are modified by the function. 1526 inline void add_dirty_regs(RegGroup group, RegMask regs) noexcept { 1527 ASMJIT_ASSERT(group <= RegGroup::kMaxVirt); 1528 _dirty_regs[group] |= regs; 1529 } 1530 1531 //! \overload 1532 inline void add_dirty_regs(const Reg& reg) noexcept { 1533 ASMJIT_ASSERT(reg.id() < Globals::kMaxPhysRegs); 1534 add_dirty_regs(reg.reg_group(), Support::bit_mask<RegMask>(reg.id())); 1535 } 1536 1537 //! \overload 1538 template<typename... Args> 1539 inline void add_dirty_regs(const Reg& reg, Args&&... args) noexcept { 1540 add_dirty_regs(reg); 1541 add_dirty_regs(std::forward<Args>(args)...); 1542 } 1543 1544 //! A helper function to set all registers from all register groups dirty. 1545 //! 1546 //! \note This should not be used in general as it's the most pessimistic case. However, it can be used for testing 1547 //! or in cases in which all registers are considered clobbered. 1548 ASMJIT_INLINE_NODEBUG void set_all_dirty() noexcept { 1549 for (size_t i = 0; i < ASMJIT_ARRAY_SIZE(_dirty_regs); i++) { 1550 _dirty_regs[i] = 0xFFFFFFFFu; 1551 } 1552 } 1553 1554 //! A helper function to set all registers from the given register `group` dirty. 1555 ASMJIT_INLINE void set_all_dirty(RegGroup group) noexcept { 1556 ASMJIT_ASSERT(group <= RegGroup::kMaxVirt); 1557 _dirty_regs[group] = 0xFFFFFFFFu; 1558 } 1559 1560 //! Returns a calculated mask of registers of the given `group` that will be saved and restored in the function's 1561 //! prolog and epilog, respectively. The register mask is calculated from both `dirty_regs` (provided by user) and 1562 //! `preserved_mask` (provided by the calling convention). 1563 [[nodiscard]] 1564 ASMJIT_INLINE RegMask saved_regs(RegGroup group) const noexcept { 1565 ASMJIT_ASSERT(group <= RegGroup::kMaxVirt); 1566 return _dirty_regs[group] & _preserved_regs[group]; 1567 } 1568 1569 //! Returns all dirty registers as a Support::Array<> type. 1570 [[nodiscard]] 1571 ASMJIT_INLINE_NODEBUG const RegMasks& dirty_regs() const noexcept { return _dirty_regs; } 1572 1573 //! Returns all preserved registers as a Support::Array<> type. 1574 [[nodiscard]] 1575 ASMJIT_INLINE_NODEBUG const RegMasks& preserved_regs() const noexcept { return _preserved_regs; } 1576 1577 //! Returns the mask of preserved registers of the given register `group`. 1578 //! 1579 //! Preserved registers are those that must survive the function call unmodified. The function can only modify 1580 //! preserved registers it they are saved and restored in function's prolog and epilog, respectively. 1581 [[nodiscard]] 1582 ASMJIT_INLINE RegMask preserved_regs(RegGroup group) const noexcept { 1583 ASMJIT_ASSERT(group <= RegGroup::kMaxVirt); 1584 return _preserved_regs[group]; 1585 } 1586 1587 //! Sets which registers (as a mask) are unavailable for allocation. 1588 //! 1589 //! \note This completely overwrites the current unavailable mask. 1590 ASMJIT_INLINE void set_unavailable_regs(RegGroup group, RegMask regs) noexcept { 1591 ASMJIT_ASSERT(group <= RegGroup::kMaxVirt); 1592 _unavailable_regs[group] = regs; 1593 } 1594 1595 //! Adds registers (as a mask) to the unavailable set. 1596 ASMJIT_INLINE void add_unavailable_regs(RegGroup group, RegMask regs) noexcept { 1597 ASMJIT_ASSERT(group <= RegGroup::kMaxVirt); 1598 _unavailable_regs[group] |= regs; 1599 } 1600 1601 //! Adds a single register to the unavailable set. 1602 ASMJIT_INLINE void add_unavailable_regs(const Reg& reg) noexcept { 1603 ASMJIT_ASSERT(reg.id() < Globals::kMaxPhysRegs); 1604 add_unavailable_regs(reg.reg_group(), Support::bit_mask<RegMask>(reg.id())); 1605 } 1606 1607 //! Adds multiple registers to the unavailable set. 1608 template<typename... Args> 1609 ASMJIT_INLINE void add_unavailable_regs(const Reg& reg, Args&&... args) noexcept { 1610 add_unavailable_regs(reg); 1611 add_unavailable_regs(std::forward<Args>(args)...); 1612 } 1613 1614 //! Clears all unavailable registers across all register groups (i.e., makes them all available again). 1615 ASMJIT_INLINE_NODEBUG void clear_unavailable_regs() noexcept { 1616 for (size_t i = 0; i < ASMJIT_ARRAY_SIZE(_unavailable_regs); i++) 1617 _unavailable_regs[i] = 0; 1618 } 1619 1620 //! Clears all unavailable registers in a specific register group. 1621 ASMJIT_INLINE void clear_unavailable_regs(RegGroup group) noexcept { 1622 ASMJIT_ASSERT(group <= RegGroup::kMaxVirt); 1623 _unavailable_regs[group] = 0; 1624 } 1625 1626 //! Returns the set of unavailable registers for the given group. 1627 [[nodiscard]] 1628 ASMJIT_INLINE RegMask unavailable_regs(RegGroup group) const noexcept { 1629 ASMJIT_ASSERT(group <= RegGroup::kMaxVirt); 1630 return _unavailable_regs[group]; 1631 } 1632 1633 //! Returns all unavailable registers as a Support::Array<>. 1634 [[nodiscard]] 1635 ASMJIT_INLINE_NODEBUG const RegMasks& unavailable_regs() const noexcept { 1636 return _unavailable_regs; 1637 } 1638 1639 //! Returns the size of a save-restore are for the required register `group`. 1640 [[nodiscard]] 1641 ASMJIT_INLINE uint32_t save_restore_reg_size(RegGroup group) const noexcept { 1642 ASMJIT_ASSERT(group <= RegGroup::kMaxVirt); 1643 return _save_restore_reg_size[group]; 1644 } 1645 1646 //! Returns the alignment that must be guaranteed to save/restore the required register `group`. 1647 [[nodiscard]] 1648 ASMJIT_INLINE uint32_t save_restore_alignment(RegGroup group) const noexcept { 1649 ASMJIT_ASSERT(group <= RegGroup::kMaxVirt); 1650 return _save_restore_alignment[group]; 1651 } 1652 1653 [[nodiscard]] 1654 ASMJIT_INLINE_NODEBUG bool has_sa_reg_id() const noexcept { return _sa_reg_id != Reg::kIdBad; } 1655 1656 [[nodiscard]] 1657 ASMJIT_INLINE_NODEBUG uint32_t sa_reg_id() const noexcept { return _sa_reg_id; } 1658 1659 ASMJIT_INLINE_NODEBUG void set_sa_reg_id(uint32_t reg_id) { _sa_reg_id = uint8_t(reg_id); } 1660 1661 ASMJIT_INLINE_NODEBUG void reset_sa_reg_id() { set_sa_reg_id(Reg::kIdBad); } 1662 1663 //! Returns stack size required to save/restore registers via push/pop. 1664 [[nodiscard]] 1665 ASMJIT_INLINE_NODEBUG uint32_t push_pop_save_size() const noexcept { return _push_pop_save_size; } 1666 1667 //! Returns an offset to the stack where registers are saved via push/pop. 1668 [[nodiscard]] 1669 ASMJIT_INLINE_NODEBUG uint32_t push_pop_save_offset() const noexcept { return _push_pop_save_offset; } 1670 1671 //! Returns stack size required to save/restore extra registers that don't use push/pop/ 1672 //! 1673 //! \note On X86 this covers all registers except GP registers, on other architectures it can be always 1674 //! zero (for example AArch64 saves all registers via push/pop like instructions, so this would be zero). 1675 [[nodiscard]] 1676 ASMJIT_INLINE_NODEBUG uint32_t extra_reg_save_size() const noexcept { return _extra_reg_save_size; } 1677 1678 //! Returns an offset to the stack where extra registers are saved. 1679 [[nodiscard]] 1680 ASMJIT_INLINE_NODEBUG uint32_t extra_reg_save_offset() const noexcept { return _extra_reg_save_offset; } 1681 1682 //! Tests whether the functions contains stack adjustment. 1683 [[nodiscard]] 1684 ASMJIT_INLINE_NODEBUG bool has_stack_adjustment() const noexcept { return _stack_adjustment != 0; } 1685 1686 //! Returns function's stack adjustment used in function's prolog and epilog. 1687 //! 1688 //! If the returned value is zero it means that the stack is not adjusted. This can mean both that the stack 1689 //! is not used and/or the stack is only adjusted by instructions that pust/pop registers into/from stack. 1690 [[nodiscard]] 1691 ASMJIT_INLINE_NODEBUG uint32_t stack_adjustment() const noexcept { return _stack_adjustment; } 1692 1693 //! \} 1694 1695 //! \name Finalization 1696 //! \{ 1697 1698 ASMJIT_API Error finalize() noexcept; 1699 1700 //! \} 1701 }; 1702 1703 //! A helper class that can be used to assign a physical register for each function argument. The assignment 1704 //! is passed to \ref BaseEmitter::emit_args_assignment() function. 1705 class FuncArgsAssignment { 1706 public: 1707 //! \name Members 1708 //! \{ 1709 1710 //! Function detail. 1711 const FuncDetail* _func_detail {}; 1712 //! Register that can be used to access arguments passed by stack. 1713 uint8_t _sa_reg_id = uint8_t(Reg::kIdBad); 1714 //! Reserved for future use. 1715 uint8_t _reserved[3] {}; 1716 //! Mapping of each function argument. 1717 FuncValuePack _arg_packs[Globals::kMaxFuncArgs] {}; 1718 1719 //! \} 1720 1721 //! \name Construction & Destruction 1722 //! \{ 1723 1724 //! Creates either a default initialized `FuncArgsAssignment` or to assignment that links to `fd`, if non-null. 1725 ASMJIT_INLINE_NODEBUG explicit FuncArgsAssignment(const FuncDetail* fd = nullptr) noexcept 1726 : _func_detail(fd), 1727 _sa_reg_id(uint8_t(Reg::kIdBad)) {} 1728 1729 //! Copy constructor. 1730 ASMJIT_INLINE_NODEBUG FuncArgsAssignment(const FuncArgsAssignment& other) noexcept = default; 1731 1732 //! Resets this `FuncArgsAssignment` to either default constructed state or to assignment that links to `fd`, 1733 //! if non-null. 1734 ASMJIT_INLINE void reset(const FuncDetail* fd = nullptr) noexcept { 1735 _func_detail = fd; 1736 _sa_reg_id = uint8_t(Reg::kIdBad); 1737 memset(_reserved, 0, sizeof(_reserved)); 1738 memset(_arg_packs, 0, sizeof(_arg_packs)); 1739 } 1740 1741 //! \} 1742 1743 //! \name Overloaded Operators 1744 //! \{ 1745 1746 //! Copy assignment. 1747 ASMJIT_INLINE_NODEBUG FuncArgsAssignment& operator=(const FuncArgsAssignment& other) noexcept = default; 1748 1749 //! \} 1750 1751 //! \name Accessors 1752 //! \{ 1753 1754 //! Returns the associated \ref FuncDetail of this `FuncArgsAssignment`. 1755 [[nodiscard]] 1756 ASMJIT_INLINE_NODEBUG const FuncDetail* func_detail() const noexcept { return _func_detail; } 1757 1758 //! Associates \ref FuncDetail with this `FuncArgsAssignment`. 1759 ASMJIT_INLINE_NODEBUG void set_func_detail(const FuncDetail* fd) noexcept { _func_detail = fd; } 1760 1761 //! Returns whether a register to access stack arguments is available. 1762 [[nodiscard]] 1763 ASMJIT_INLINE_NODEBUG bool has_sa_reg_id() const noexcept { return _sa_reg_id != Reg::kIdBad; } 1764 1765 //! Returns a physical ID of a register that can access stack arguments. 1766 [[nodiscard]] 1767 ASMJIT_INLINE_NODEBUG uint32_t sa_reg_id() const noexcept { return _sa_reg_id; } 1768 1769 //! Sets a physical ID of a register that can access stack arguments. 1770 ASMJIT_INLINE_NODEBUG void set_sa_reg_id(uint32_t reg_id) { _sa_reg_id = uint8_t(reg_id); } 1771 1772 //! Resets a physical ID of a register that can access stack arguments. 1773 ASMJIT_INLINE_NODEBUG void reset_sa_reg_id() { _sa_reg_id = uint8_t(Reg::kIdBad); } 1774 1775 //! Returns assigned argument at `arg_index` and `value_index`. 1776 //! 1777 //! \note `arg_index` refers to he function argument and `value_index` refers to a value pack (in case multiple 1778 //! values are passed as a single argument). 1779 [[nodiscard]] 1780 ASMJIT_INLINE FuncValue& arg(size_t arg_index, size_t value_index) noexcept { 1781 ASMJIT_ASSERT(arg_index < ASMJIT_ARRAY_SIZE(_arg_packs)); 1782 return _arg_packs[arg_index][value_index]; 1783 } 1784 1785 //! \overload 1786 [[nodiscard]] 1787 ASMJIT_INLINE const FuncValue& arg(size_t arg_index, size_t value_index) const noexcept { 1788 ASMJIT_ASSERT(arg_index < ASMJIT_ARRAY_SIZE(_arg_packs)); 1789 return _arg_packs[arg_index][value_index]; 1790 } 1791 1792 //! Tests whether argument at `arg_index` and `value_index` has been assigned. 1793 [[nodiscard]] 1794 ASMJIT_INLINE bool is_assigned(size_t arg_index, size_t value_index) const noexcept { 1795 ASMJIT_ASSERT(arg_index < ASMJIT_ARRAY_SIZE(_arg_packs)); 1796 return _arg_packs[arg_index][value_index].is_assigned(); 1797 } 1798 1799 //! Assigns register at `arg_index` and value index of 0 to `reg` and an optional `type_id`. 1800 ASMJIT_INLINE void assign_reg(size_t arg_index, const Reg& reg, TypeId type_id = TypeId::kVoid) noexcept { 1801 ASMJIT_ASSERT(arg_index < ASMJIT_ARRAY_SIZE(_arg_packs)); 1802 ASMJIT_ASSERT(reg.is_phys_reg()); 1803 _arg_packs[arg_index][0].init_reg(reg.reg_type(), reg.id(), type_id); 1804 } 1805 1806 //! Assigns register at `arg_index` and value index of 0 to `reg_type`, `reg_id`, and an optional `type_id`. 1807 ASMJIT_INLINE void assign_reg(size_t arg_index, RegType reg_type, uint32_t reg_id, TypeId type_id = TypeId::kVoid) noexcept { 1808 ASMJIT_ASSERT(arg_index < ASMJIT_ARRAY_SIZE(_arg_packs)); 1809 _arg_packs[arg_index][0].init_reg(reg_type, reg_id, type_id); 1810 } 1811 1812 //! Assigns stack at `arg_index` and value index of 0 to `offset` and an optional `type_id`. 1813 ASMJIT_INLINE void assign_stack(size_t arg_index, int32_t offset, TypeId type_id = TypeId::kVoid) noexcept { 1814 ASMJIT_ASSERT(arg_index < ASMJIT_ARRAY_SIZE(_arg_packs)); 1815 _arg_packs[arg_index][0].init_stack(offset, type_id); 1816 } 1817 1818 //! Assigns register at `arg_index` and `value_index` to `reg` and an optional `type_id`. 1819 ASMJIT_INLINE void assign_reg_in_pack(size_t arg_index, size_t value_index, const Reg& reg, TypeId type_id = TypeId::kVoid) noexcept { 1820 ASMJIT_ASSERT(arg_index < ASMJIT_ARRAY_SIZE(_arg_packs)); 1821 ASMJIT_ASSERT(reg.is_phys_reg()); 1822 _arg_packs[arg_index][value_index].init_reg(reg.reg_type(), reg.id(), type_id); 1823 } 1824 1825 //! Assigns register at `arg_index` and `value_index` to `reg_type`, `reg_id`, and an optional `type_id`. 1826 ASMJIT_INLINE void assign_reg_in_pack(size_t arg_index, size_t value_index, RegType reg_type, uint32_t reg_id, TypeId type_id = TypeId::kVoid) noexcept { 1827 ASMJIT_ASSERT(arg_index < ASMJIT_ARRAY_SIZE(_arg_packs)); 1828 _arg_packs[arg_index][value_index].init_reg(reg_type, reg_id, type_id); 1829 } 1830 1831 //! Assigns stack at `arg_index` and `value_index` to `offset` and an optional `type_id`. 1832 ASMJIT_INLINE void assign_stack_in_pack(size_t arg_index, size_t value_index, int32_t offset, TypeId type_id = TypeId::kVoid) noexcept { 1833 ASMJIT_ASSERT(arg_index < ASMJIT_ARRAY_SIZE(_arg_packs)); 1834 _arg_packs[arg_index][value_index].init_stack(offset, type_id); 1835 } 1836 1837 //! \cond INTERNAL 1838 // NOTE: All `assign_all()` methods are shortcuts to assign all arguments at once, however, since registers are 1839 // passed all at once these initializers don't provide any way to pass TypeId and/or to keep any argument between 1840 // the arguments passed unassigned. 1841 ASMJIT_INLINE void _assign_all_internal(size_t arg_index, const Reg& reg) noexcept { 1842 assign_reg(arg_index, reg); 1843 } 1844 1845 template<typename... Args> 1846 ASMJIT_INLINE void _assign_all_internal(size_t arg_index, const Reg& reg, Args&&... args) noexcept { 1847 assign_reg(arg_index, reg); 1848 _assign_all_internal(arg_index + 1, std::forward<Args>(args)...); 1849 } 1850 //! \endcond 1851 1852 //! Assigns all argument at once. 1853 //! 1854 //! \note This function can be only used if the arguments don't contain value packs (multiple values per argument). 1855 template<typename... Args> 1856 ASMJIT_INLINE void assign_all(Args&&... args) noexcept { 1857 _assign_all_internal(0, std::forward<Args>(args)...); 1858 } 1859 1860 //! \} 1861 1862 //! \name Utilities 1863 //! \{ 1864 1865 //! Update `FuncFrame` based on function's arguments assignment. 1866 //! 1867 //! \note This function must be called in order to use `BaseEmitter::emit_args_assignment()`, otherwise the \ref FuncFrame 1868 //! would not contain the information necessary to assign all arguments into the registers and/or stack specified. 1869 ASMJIT_API Error update_func_frame(FuncFrame& frame) const noexcept; 1870 1871 //! \} 1872 }; 1873 1874 //! \} 1875 1876 ASMJIT_END_NAMESPACE 1877 1878 #endif // ASMJIT_CORE_FUNC_H_INCLUDED