odin-blend2d

Odin bindings to Blend2D
Log | Files | Refs | README | LICENSE

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