LCOV - code coverage report
Current view: top level - asmjit - func.h (source / functions) Hit Total Coverage
Test: plumed test coverage (other modules) Lines: 88 126 69.8 %
Date: 2026-11-05 10:19:06 Functions: 6 8 75.0 %

          Line data    Source code
       1             : #ifdef __PLUMED_HAS_ASMJIT
       2             : // This file is part of AsmJit project <https://asmjit.com>
       3             : //
       4             : // See asmjit.h or LICENSE.md for license and copyright information
       5             : // SPDX-License-Identifier: Zlib
       6             : 
       7             : #ifndef ASMJIT_CORE_FUNC_H_INCLUDED
       8             : #define ASMJIT_CORE_FUNC_H_INCLUDED
       9             : 
      10             : #include "archtraits.h"
      11             : #include "environment.h"
      12             : #include "operand.h"
      13             : #include "type.h"
      14             : #include "support.h"
      15             : 
      16             : ASMJIT_BEGIN_NAMESPACE
      17             : 
      18             : //! \addtogroup asmjit_function
      19             : //! \{
      20             : 
      21             : //! Calling convention id.
      22             : //!
      23             : //! Calling conventions can be divided into the following groups:
      24             : //!
      25             : //!   - Universal - calling conventions are applicable to any target. They will be converted to a target dependent
      26             : //!     calling convention at runtime by \ref CallConv::init() with some help from \ref Environment. The purpose of
      27             : //!     these calling conventions is to make using functions less target dependent and closer to C and C++.
      28             : //!
      29             : //!   - Target specific - calling conventions that are used by a particular architecture and ABI. For example
      30             : //!     Windows 64-bit calling convention and AMD64 SystemV calling convention.
      31             : enum class CallConvId : uint8_t {
      32             :   //! None or invalid (can't be used).
      33             :   kNone = 0,
      34             : 
      35             :   // Universal Calling Conventions
      36             :   // -----------------------------
      37             : 
      38             :   //! Standard function call or explicit `__cdecl` where it can be specified.
      39             :   //!
      40             :   //! This is a universal calling convention, which is used to initialize specific calling connventions based on
      41             :   //! architecture, platform, and its ABI.
      42             :   kCDecl = 1,
      43             : 
      44             :   //! `__stdcall` on targets that support this calling convention (X86).
      45             :   //!
      46             :   //! \note This calling convention is only supported on 32-bit X86. If used on environment that doesn't support
      47             :   //! this calling convention it will be replaced by \ref CallConvId::kCDecl.
      48             :   kStdCall = 2,
      49             : 
      50             :   //! `__fastcall` on targets that support this calling convention (X86).
      51             :   //!
      52             :   //! \note This calling convention is only supported on 32-bit X86. If used on environment that doesn't support
      53             :   //! this calling convention it will be replaced by \ref CallConvId::kCDecl.
      54             :   kFastCall = 3,
      55             : 
      56             :   //! `__vectorcall` on targets that support this calling convention (X86/X64).
      57             :   //!
      58             :   //! \note This calling convention is only supported on 32-bit and 64-bit X86 architecture on Windows platform.
      59             :   //! If used on environment that doesn't support this calling it will be replaced by \ref CallConvId::kCDecl.
      60             :   kVectorCall = 4,
      61             : 
      62             :   //! `__thiscall` on targets that support this calling convention (X86).
      63             :   //!
      64             :   //! \note This calling convention is only supported on 32-bit X86 Windows platform. If used on environment that
      65             :   //! doesn't support this calling convention it will be replaced by \ref CallConvId::kCDecl.
      66             :   kThisCall = 5,
      67             : 
      68             :   //! `__attribute__((regparm(1)))` convention (GCC and Clang).
      69             :   kRegParm1 = 6,
      70             :   //! `__attribute__((regparm(2)))` convention (GCC and Clang).
      71             :   kRegParm2 = 7,
      72             :   //! `__attribute__((regparm(3)))` convention (GCC and Clang).
      73             :   kRegParm3 = 8,
      74             : 
      75             :   //! Soft-float calling convention (ARM).
      76             :   //!
      77             :   //! Floating point arguments are passed via general purpose registers.
      78             :   kSoftFloat = 9,
      79             : 
      80             :   //! Hard-float calling convention (ARM).
      81             :   //!
      82             :   //! Floating point arguments are passed via SIMD registers.
      83             :   kHardFloat = 10,
      84             : 
      85             :   //! AsmJit specific calling convention designed for calling functions inside a multimedia code that don't use many
      86             :   //! registers internally, but are long enough to be called and not inlined. These functions are usually used to
      87             :   //! calculate trigonometric functions, logarithms, etc...
      88             :   kLightCall2 = 16,
      89             :   kLightCall3 = 17,
      90             :   kLightCall4 = 18,
      91             : 
      92             :   // ABI-Specific Calling Conventions
      93             :   // --------------------------------
      94             : 
      95             :   //! X64 System-V calling convention.
      96             :   kX64SystemV = 32,
      97             :   //! X64 Windows calling convention.
      98             :   kX64Windows = 33,
      99             : 
     100             :   //! Maximum value of `CallConvId`.
     101             :   kMaxValue = kX64Windows,
     102             : 
     103             :   // Host Calling Conventions
     104             :   // ------------------------
     105             : 
     106             :   //! Host calling convention detected at compile-time.
     107             :   kHost =
     108             : #if defined(_DOXYGEN)
     109             :     DETECTED_AT_COMPILE_TIME
     110             : #elif ASMJIT_ARCH_ARM == 32 && defined(__SOFTFP__)
     111             :     kSoftFloat
     112             : #elif ASMJIT_ARCH_ARM == 32 && !defined(__SOFTFP__)
     113             :     kHardFloat
     114             : #else
     115             :     kCDecl
     116             : #endif
     117             : };
     118             : 
     119             : //! Strategy used by calling conventions to assign registers to function arguments.
     120             : //!
     121             : //! Calling convention strategy describes how AsmJit should convert function arguments used by \ref FuncSignature
     122             : //! into register identifiers and stack offsets. The \ref CallConvStrategy::kDefault strategy assigns registers
     123             : //! and then stack whereas \ref CallConvStrategy::kX64Windows strategy does register shadowing as defined by WIN64
     124             : //! calling convention, which is only used by 64-bit Windows.
     125             : enum class CallConvStrategy : uint8_t {
     126             :   //! Default register assignment strategy.
     127             :   kDefault = 0,
     128             :   //! Windows 64-bit ABI register assignment strategy.
     129             :   kX64Windows = 1,
     130             :   //! Windows 64-bit __vectorcall register assignment strategy.
     131             :   kX64VectorCall = 2,
     132             : 
     133             :   //! Maximum value of `CallConvStrategy`.
     134             :   kMaxValue = kX64VectorCall
     135             : };
     136             : 
     137             : //! Calling convention flags.
     138             : enum class CallConvFlags : uint32_t {
     139             :   //! No flags.
     140             :   kNone = 0,
     141             :   //! Callee is responsible for cleaning up the stack.
     142             :   kCalleePopsStack = 0x0001u,
     143             :   //! Pass vector arguments indirectly (as a pointer).
     144             :   kIndirectVecArgs = 0x0002u,
     145             :   //! Pass F32 and F64 arguments via VEC128 register.
     146             :   kPassFloatsByVec = 0x0004u,
     147             :   //! Pass MMX and vector arguments via stack if the function has variable arguments.
     148             :   kPassVecByStackIfVA = 0x0008u,
     149             :   //! MMX registers are passed and returned via GP registers.
     150             :   kPassMmxByGp = 0x0010u,
     151             :   //! MMX registers are passed and returned via XMM registers.
     152             :   kPassMmxByXmm = 0x0020u,
     153             :   //! Calling convention can be used with variable arguments.
     154             :   kVarArgCompatible = 0x0080u
     155             : };
     156           0 : ASMJIT_DEFINE_ENUM_FLAGS(CallConvFlags)
     157             : 
     158             : //! Function calling convention.
     159             : //!
     160             : //! Function calling convention is a scheme that defines how function parameters are passed and how function
     161             : //! returns its result. AsmJit defines a variety of architecture and OS specific calling conventions and also
     162             : //! provides a compile time detection to make the code-generation easier.
     163             : struct CallConv {
     164             :   //! \name Constants
     165             :   //! \{
     166             : 
     167             :   enum : uint32_t {
     168             :     //! Maximum number of register arguments per register group.
     169             :     //!
     170             :     //! \note This is not really AsmJit's limitatation, it's just the number that makes sense considering all common
     171             :     //! calling conventions. Usually even conventions that use registers to pass function arguments are limited to 8
     172             :     //! and less arguments passed via registers per group.
     173             :     kMaxRegArgsPerGroup = 16
     174             :   };
     175             : 
     176             :   //! \}
     177             : 
     178             :   //! \name Members
     179             :   //! \{
     180             : 
     181             :   //! Target architecture.
     182             :   Arch _arch;
     183             :   //! Calling convention id.
     184             :   CallConvId _id;
     185             :   //! Register assignment strategy.
     186             :   CallConvStrategy _strategy;
     187             : 
     188             :   //! Red zone size (AMD64 == 128 bytes).
     189             :   uint8_t _redZoneSize;
     190             :   //! Spill zone size (WIN-X64 == 32 bytes).
     191             :   uint8_t _spillZoneSize;
     192             :   //! Natural stack alignment as defined by OS/ABI.
     193             :   uint8_t _naturalStackAlignment;
     194             : 
     195             :   //! Calling convention flags.
     196             :   CallConvFlags _flags;
     197             : 
     198             :   //! Size to save/restore per register group.
     199             :   Support::Array<uint8_t, Globals::kNumVirtGroups> _saveRestoreRegSize;
     200             :   //! Alignment of save/restore groups.
     201             :   Support::Array<uint8_t, Globals::kNumVirtGroups> _saveRestoreAlignment;
     202             : 
     203             :   //! Mask of all passed registers, per group.
     204             :   Support::Array<RegMask, Globals::kNumVirtGroups> _passedRegs;
     205             :   //! Mask of all preserved registers, per group.
     206             :   Support::Array<RegMask, Globals::kNumVirtGroups> _preservedRegs;
     207             : 
     208             :   //! Passed registers' order.
     209             :   union RegOrder {
     210             :     //! Passed registers, ordered.
     211             :     uint8_t id[kMaxRegArgsPerGroup];
     212             :     //! Packed IDs in `uint32_t` array.
     213             :     uint32_t packed[(kMaxRegArgsPerGroup + 3) / 4];
     214             :   };
     215             : 
     216             :   //! Passed registers' order, per register group.
     217             :   Support::Array<RegOrder, Globals::kNumVirtGroups> _passedOrder;
     218             : 
     219             :   //! \}
     220             : 
     221             :   //! \name Construction & Destruction
     222             :   //! \{
     223             : 
     224             :   //! Initializes this calling convention to the given `ccId` based on the `environment`.
     225             :   //!
     226             :   //! See \ref CallConvId and \ref Environment for more details.
     227             :   ASMJIT_API Error init(CallConvId ccId, const Environment& environment) noexcept;
     228             : 
     229             :   //! Resets this CallConv struct into a defined state.
     230             :   //!
     231             :   //! It's recommended to reset the \ref CallConv struct in case you would like create a custom calling convention
     232             :   //! as it prevents from using an uninitialized data (CallConv doesn't have a constructor that would initialize it,
     233             :   //! it's just a struct).
     234       37131 :   inline void reset() noexcept {
     235             :     memset(this, 0, sizeof(*this));
     236             :     memset(_passedOrder.data(), 0xFF, sizeof(_passedOrder));
     237       37131 :   }
     238             : 
     239             :   //! \}
     240             : 
     241             :   //! \name Accessors
     242             :   //! \{
     243             : 
     244             :   //! Returns the target architecture of this calling convention.
     245      101942 :   inline Arch arch() const noexcept { return _arch; }
     246             :   //! Sets the target architecture of this calling convention.
     247       37131 :   inline void setArch(Arch arch) noexcept { _arch = arch; }
     248             : 
     249             :   //! Returns the calling convention id.
     250           0 :   inline CallConvId id() const noexcept { return _id; }
     251             :   //! Sets the calling convention id.
     252       37131 :   inline void setId(CallConvId ccId) noexcept { _id = ccId; }
     253             : 
     254             :   //! Returns the strategy used to assign registers to arguments.
     255       37131 :   inline CallConvStrategy strategy() const noexcept { return _strategy; }
     256             :   //! Sets the strategy used to assign registers to arguments.
     257           0 :   inline void setStrategy(CallConvStrategy ccStrategy) noexcept { _strategy = ccStrategy; }
     258             : 
     259             :   //! Tests whether the calling convention has the given `flag` set.
     260       46816 :   inline bool hasFlag(CallConvFlags flag) const noexcept { return Support::test(_flags, flag); }
     261             :   //! Returns the calling convention flags, see `Flags`.
     262             :   inline CallConvFlags flags() const noexcept { return _flags; }
     263             :   //! Adds the calling convention flags, see `Flags`.
     264           0 :   inline void setFlags(CallConvFlags flag) noexcept { _flags = flag; };
     265             :   //! Adds the calling convention flags, see `Flags`.
     266           0 :   inline void addFlags(CallConvFlags flags) noexcept { _flags |= flags; };
     267             : 
     268             :   //! Tests whether this calling convention specifies 'RedZone'.
     269             :   inline bool hasRedZone() const noexcept { return _redZoneSize != 0; }
     270             :   //! Tests whether this calling convention specifies 'SpillZone'.
     271             :   inline bool hasSpillZone() const noexcept { return _spillZoneSize != 0; }
     272             : 
     273             :   //! Returns size of 'RedZone'.
     274       27492 :   inline uint32_t redZoneSize() const noexcept { return _redZoneSize; }
     275             :   //! Returns size of 'SpillZone'.
     276       27492 :   inline uint32_t spillZoneSize() const noexcept { return _spillZoneSize; }
     277             : 
     278             :   //! Sets size of 'RedZone'.
     279       37131 :   inline void setRedZoneSize(uint32_t size) noexcept { _redZoneSize = uint8_t(size); }
     280             :   //! Sets size of 'SpillZone'.
     281           0 :   inline void setSpillZoneSize(uint32_t size) noexcept { _spillZoneSize = uint8_t(size); }
     282             : 
     283             :   //! Returns a natural stack alignment.
     284       54984 :   inline uint32_t naturalStackAlignment() const noexcept { return _naturalStackAlignment; }
     285             :   //! Sets a natural stack alignment.
     286             :   //!
     287             :   //! This function can be used to override the default stack alignment in case that you know that it's alignment is
     288             :   //! different. For example it allows to implement custom calling conventions that guarantee higher stack alignment.
     289           0 :   inline void setNaturalStackAlignment(uint32_t value) noexcept { _naturalStackAlignment = uint8_t(value); }
     290             : 
     291             :   //! Returns the size of a register (or its part) to be saved and restored of the given `group`.
     292           0 :   inline uint32_t saveRestoreRegSize(RegGroup group) const noexcept { return _saveRestoreRegSize[group]; }
     293             :   //! Sets the size of a vector register (or its part) to be saved and restored.
     294       74262 :   inline void setSaveRestoreRegSize(RegGroup group, uint32_t size) noexcept { _saveRestoreRegSize[group] = uint8_t(size); }
     295             : 
     296             :   //! Returns the alignment of a save-restore area of the given `group`.
     297             :   inline uint32_t saveRestoreAlignment(RegGroup group) const noexcept { return _saveRestoreAlignment[group]; }
     298             :   //! Sets the alignment of a save-restore area of the given `group`.
     299       37131 :   inline void setSaveRestoreAlignment(RegGroup group, uint32_t alignment) noexcept { _saveRestoreAlignment[group] = uint8_t(alignment); }
     300             : 
     301             :   //! Returns the order of passed registers of the given `group`.
     302             :   inline const uint8_t* passedOrder(RegGroup group) const noexcept {
     303             :     ASMJIT_ASSERT(group <= RegGroup::kMaxVirt);
     304           0 :     return _passedOrder[size_t(group)].id;
     305             :   }
     306             : 
     307             :   //! Returns the mask of passed registers of the given `group`.
     308             :   inline RegMask passedRegs(RegGroup group) const noexcept {
     309             :     ASMJIT_ASSERT(group <= RegGroup::kMaxVirt);
     310             :     return _passedRegs[size_t(group)];
     311             :   }
     312             : 
     313             :   inline void _setPassedPacked(RegGroup group, uint32_t p0, uint32_t p1, uint32_t p2, uint32_t p3) noexcept {
     314             :     ASMJIT_ASSERT(group <= RegGroup::kMaxVirt);
     315             : 
     316       37131 :     _passedOrder[group].packed[0] = p0;
     317       37131 :     _passedOrder[group].packed[1] = p1;
     318       37131 :     _passedOrder[group].packed[2] = p2;
     319       37131 :     _passedOrder[group].packed[3] = p3;
     320             :   }
     321             : 
     322             :   //! Resets the order and mask of passed registers.
     323             :   inline void setPassedToNone(RegGroup group) noexcept {
     324             :     ASMJIT_ASSERT(group <= RegGroup::kMaxVirt);
     325             : 
     326             :     _setPassedPacked(group, 0xFFFFFFFFu, 0xFFFFFFFFu, 0xFFFFFFFFu, 0xFFFFFFFFu);
     327             :     _passedRegs[size_t(group)] = 0u;
     328             :   }
     329             : 
     330             :   //! Sets the order and mask of passed registers.
     331             :   inline void setPassedOrder(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 {
     332             :     ASMJIT_ASSERT(group <= RegGroup::kMaxVirt);
     333             : 
     334             :     // NOTE: This should always be called with all arguments known at compile time, so even if it looks scary it
     335             :     // should be translated into few instructions.
     336             :     _setPassedPacked(group, Support::bytepack32_4x8(a0, a1, a2, a3),
     337             :                             Support::bytepack32_4x8(a4, a5, a6, a7),
     338             :                             0xFFFFFFFFu,
     339             :                             0xFFFFFFFFu);
     340             : 
     341           0 :     _passedRegs[group] = (a0 != 0xFF ? 1u << a0 : 0u) |
     342             :                          (a1 != 0xFF ? 1u << a1 : 0u) |
     343             :                          (a2 != 0xFF ? 1u << a2 : 0u) |
     344             :                          (a3 != 0xFF ? 1u << a3 : 0u) |
     345             :                          (a4 != 0xFF ? 1u << a4 : 0u) |
     346             :                          (a5 != 0xFF ? 1u << a5 : 0u) |
     347             :                          (a6 != 0xFF ? 1u << a6 : 0u) |
     348             :                          (a7 != 0xFF ? 1u << a7 : 0u) ;
     349             :   }
     350             : 
     351             :   //! Returns preserved register mask of the given `group`.
     352             :   inline RegMask preservedRegs(RegGroup group) const noexcept {
     353             :     ASMJIT_ASSERT(group <= RegGroup::kMaxVirt);
     354      158163 :     return _preservedRegs[group];
     355             :   }
     356             : 
     357             :   //! Sets preserved register mask of the given `group`.
     358             :   inline void setPreservedRegs(RegGroup group, RegMask regs) noexcept {
     359             :     ASMJIT_ASSERT(group <= RegGroup::kMaxVirt);
     360           0 :     _preservedRegs[group] = regs;
     361       37131 :   }
     362             : 
     363             :   //! \}
     364             : };
     365             : 
     366             : //! Function signature.
     367             : //!
     368             : //! Contains information about function return type, count of arguments and their TypeIds. Function signature is
     369             : //! a low level structure which doesn't contain platform specific or calling convention specific information.
     370             : struct FuncSignature {
     371             :   //! \name Constants
     372             :   //! \{
     373             : 
     374             :   enum : uint8_t {
     375             :     //! Doesn't have variable number of arguments (`...`).
     376             :     kNoVarArgs = 0xFFu
     377             :   };
     378             : 
     379             :   //! \}
     380             : 
     381             :   //! \name Members
     382             :   //! \{
     383             : 
     384             :   //! Calling convention id.
     385             :   CallConvId _ccId;
     386             :   //! Count of arguments.
     387             :   uint8_t _argCount;
     388             :   //! Index of a first VA or `kNoVarArgs`.
     389             :   uint8_t _vaIndex;
     390             :   //! Return value TypeId.
     391             :   TypeId _ret;
     392             :   //! Function arguments TypeIds.
     393             :   const TypeId* _args;
     394             : 
     395             :   //! \}
     396             : 
     397             :   //! \name Initializtion & Reset
     398             :   //! \{
     399             : 
     400             :   //! Initializes the function signature.
     401             :   inline void init(CallConvId ccId, uint32_t vaIndex, TypeId ret, const TypeId* args, uint32_t argCount) noexcept {
     402             :     ASMJIT_ASSERT(argCount <= 0xFF);
     403             : 
     404       37131 :     _ccId = ccId;
     405       37131 :     _argCount = uint8_t(argCount);
     406       37131 :     _vaIndex = uint8_t(vaIndex);
     407       37131 :     _ret = ret;
     408       37131 :     _args = args;
     409             :   }
     410             : 
     411             :   inline void reset() noexcept { memset(this, 0, sizeof(*this)); }
     412             : 
     413             :   //! \}
     414             : 
     415             :   //! \name Accessors
     416             :   //! \{
     417             : 
     418             :   //! Returns the calling convention.
     419       37131 :   inline CallConvId callConvId() const noexcept { return _ccId; }
     420             :   //! Sets the calling convention to `ccId`;
     421             :   inline void setCallConvId(CallConvId ccId) noexcept { _ccId = ccId; }
     422             : 
     423             :   //! Tests whether the function has variable number of arguments (...).
     424           0 :   inline bool hasVarArgs() const noexcept { return _vaIndex != kNoVarArgs; }
     425             :   //! Returns the variable arguments (...) index, `kNoVarArgs` if none.
     426       37131 :   inline uint32_t vaIndex() const noexcept { return _vaIndex; }
     427             :   //! Sets the variable arguments (...) index to `index`.
     428             :   inline void setVaIndex(uint32_t index) noexcept { _vaIndex = uint8_t(index); }
     429             :   //! Resets the variable arguments index (making it a non-va function).
     430             :   inline void resetVaIndex() noexcept { _vaIndex = kNoVarArgs; }
     431             : 
     432             :   //! Returns the number of function arguments.
     433       46770 :   inline uint32_t argCount() const noexcept { return _argCount; }
     434             : 
     435             :   inline bool hasRet() const noexcept { return _ret != TypeId::kVoid; }
     436             :   //! Returns the return value type.
     437       37131 :   inline TypeId ret() const noexcept { return _ret; }
     438             : 
     439             :   //! Returns the type of the argument at index `i`.
     440             :   inline TypeId arg(uint32_t i) const noexcept {
     441             :     ASMJIT_ASSERT(i < _argCount);
     442             :     return _args[i];
     443             :   }
     444             :   //! Returns the array of function arguments' types.
     445       37131 :   inline const TypeId* args() const noexcept { return _args; }
     446             : 
     447             :   //! \}
     448             : };
     449             : 
     450             : template<typename... RET_ARGS>
     451             : class FuncSignatureT : public FuncSignature {
     452             : public:
     453       37131 :   inline FuncSignatureT(CallConvId ccId = CallConvId::kHost, uint32_t vaIndex = kNoVarArgs) noexcept {
     454             :     static constexpr TypeId ret_args[] = { (TypeId(TypeUtils::TypeIdOfT<RET_ARGS>::kTypeId))... };
     455             :     init(ccId, vaIndex, ret_args[0], ret_args + 1, uint32_t(ASMJIT_ARRAY_SIZE(ret_args) - 1));
     456             :   }
     457             : };
     458             : 
     459             : //! Function signature builder.
     460             : class FuncSignatureBuilder : public FuncSignature {
     461             : public:
     462             :   TypeId _builderArgList[Globals::kMaxFuncArgs];
     463             : 
     464             :   //! \name Initializtion & Reset
     465             :   //! \{
     466             : 
     467             :   inline FuncSignatureBuilder(CallConvId ccId = CallConvId::kHost, uint32_t vaIndex = kNoVarArgs) noexcept {
     468             :     init(ccId, vaIndex, TypeId::kVoid, _builderArgList, 0);
     469             :   }
     470             : 
     471             :   //! \}
     472             : 
     473             :   //! \name Accessors
     474             :   //! \{
     475             : 
     476             :   //! Sets the return type to `retType`.
     477             :   inline void setRet(TypeId retType) noexcept { _ret = retType; }
     478             :   //! Sets the return type based on `T`.
     479             :   template<typename T>
     480             :   inline void setRetT() noexcept { setRet(TypeId(TypeUtils::TypeIdOfT<T>::kTypeId)); }
     481             : 
     482             :   //! Sets the argument at index `index` to `argType`.
     483             :   inline void setArg(uint32_t index, TypeId argType) noexcept {
     484             :     ASMJIT_ASSERT(index < _argCount);
     485             :     _builderArgList[index] = argType;
     486             :   }
     487             :   //! Sets the argument at index `i` to the type based on `T`.
     488             :   template<typename T>
     489             :   inline void setArgT(uint32_t index) noexcept { setArg(index, TypeId(TypeUtils::TypeIdOfT<T>::kTypeId)); }
     490             : 
     491             :   //! Appends an argument of `type` to the function prototype.
     492             :   inline void addArg(TypeId type) noexcept {
     493             :     ASMJIT_ASSERT(_argCount < Globals::kMaxFuncArgs);
     494             :     _builderArgList[_argCount++] = type;
     495             :   }
     496             :   //! Appends an argument of type based on `T` to the function prototype.
     497             :   template<typename T>
     498             :   inline void addArgT() noexcept { addArg(TypeId(TypeUtils::TypeIdOfT<T>::kTypeId)); }
     499             : 
     500             :   //! \}
     501             : };
     502             : 
     503             : //! Argument or return value (or its part) as defined by `FuncSignature`, but with register or stack address
     504             : //! (and other metadata) assigned.
     505             : struct FuncValue {
     506             :   //! \name Constants
     507             :   //! \{
     508             : 
     509             :   enum Bits : uint32_t {
     510             :     kTypeIdShift      = 0,             //!< TypeId shift.
     511             :     kTypeIdMask       = 0x000000FFu,   //!< TypeId mask.
     512             : 
     513             :     kFlagIsReg        = 0x00000100u,   //!< Passed by register.
     514             :     kFlagIsStack      = 0x00000200u,   //!< Passed by stack.
     515             :     kFlagIsIndirect   = 0x00000400u,   //!< Passed indirectly by reference (internally a pointer).
     516             :     kFlagIsDone       = 0x00000800u,   //!< Used internally by arguments allocator.
     517             : 
     518             :     kStackOffsetShift = 12,            //!< Stack offset shift.
     519             :     kStackOffsetMask  = 0xFFFFF000u,   //!< Stack offset mask (must occupy MSB bits).
     520             : 
     521             :     kRegIdShift       = 16,            //!< RegId shift.
     522             :     kRegIdMask        = 0x00FF0000u,   //!< RegId mask.
     523             : 
     524             :     kRegTypeShift     = 24,            //!< RegType shift.
     525             :     kRegTypeMask      = 0xFF000000u    //!< RegType mask.
     526             :   };
     527             : 
     528             :   //! \}
     529             : 
     530             :   //! \name Members
     531             :   //! \{
     532             : 
     533             :   uint32_t _data;
     534             : 
     535             :   //! \}
     536             : 
     537             :   //! \name Initializtion & Reset
     538             :   //!
     539             :   //! These initialize the whole `FuncValue` to either register or stack. Useful when you know all of these
     540             :   //! properties and wanna just set it up.
     541             :   //!
     542             :   //! \{
     543             : 
     544             :   //! Initializes the `typeId` of this `FuncValue`.
     545             :   inline void initTypeId(TypeId typeId) noexcept {
     546       19558 :     _data = uint32_t(typeId) << kTypeIdShift;
     547       37131 :   }
     548             : 
     549       37131 :   inline void initReg(RegType regType, uint32_t regId, TypeId typeId, uint32_t flags = 0) noexcept {
     550       37131 :     _data = (uint32_t(regType) << kRegTypeShift) | (regId << kRegIdShift) | (uint32_t(typeId) << kTypeIdShift) | kFlagIsReg | flags;
     551           0 :   }
     552             : 
     553             :   inline void initStack(int32_t offset, TypeId typeId) noexcept {
     554             :     _data = (uint32_t(offset) << kStackOffsetShift) | (uint32_t(typeId) << kTypeIdShift) | kFlagIsStack;
     555             :   }
     556             : 
     557             :   //! Resets the value to its unassigned state.
     558           0 :   inline void reset() noexcept { _data = 0; }
     559             : 
     560             :   //! \}
     561             : 
     562             :   //! \name Assign
     563             :   //!
     564             :   //! These initialize only part of `FuncValue`, useful when building `FuncValue` incrementally. The caller
     565             :   //! should first init the type-id by caliing `initTypeId` and then continue building either register or stack.
     566             :   //!
     567             :   //! \{
     568             : 
     569        9873 :   inline void assignRegData(RegType regType, uint32_t regId) noexcept {
     570             :     ASMJIT_ASSERT((_data & (kRegTypeMask | kRegIdMask)) == 0);
     571        9873 :     _data |= (uint32_t(regType) << kRegTypeShift) | (regId << kRegIdShift) | kFlagIsReg;
     572        9873 :   }
     573             : 
     574           0 :   inline void assignStackOffset(int32_t offset) noexcept {
     575             :     ASMJIT_ASSERT((_data & kStackOffsetMask) == 0);
     576           0 :     _data |= (uint32_t(offset) << kStackOffsetShift) | kFlagIsStack;
     577           0 :   }
     578             : 
     579             :   //! \}
     580             : 
     581             :   //! \name Accessors
     582             :   //! \{
     583             : 
     584             :   //! Returns true if the value is initialized (explicit bool cast).
     585      164310 :   inline explicit operator bool() const noexcept { return _data != 0; }
     586             : 
     587           0 :   inline void _replaceValue(uint32_t mask, uint32_t value) noexcept { _data = (_data & ~mask) | value; }
     588             : 
     589             :   //! Tests whether the `FuncValue` has a flag `flag` set.
     590     3585304 :   inline bool hasFlag(uint32_t flag) const noexcept { return Support::test(_data, flag); }
     591             :   //! Adds `flags` to `FuncValue`.
     592           0 :   inline void addFlags(uint32_t flags) noexcept { _data |= flags; }
     593             :   //! Clears `flags` of `FuncValue`.
     594             :   inline void clearFlags(uint32_t flags) noexcept { _data &= ~flags; }
     595             : 
     596             :   //! Tests whether the value is initialized (i.e. contains a valid data).
     597           0 :   inline bool isInitialized() const noexcept { return _data != 0; }
     598             :   //! Tests whether the argument is passed by register.
     599             :   inline bool isReg() const noexcept { return hasFlag(kFlagIsReg); }
     600             :   //! Tests whether the argument is passed by stack.
     601             :   inline bool isStack() const noexcept { return hasFlag(kFlagIsStack); }
     602             :   //! Tests whether the argument is passed by register.
     603             :   inline bool isAssigned() const noexcept { return hasFlag(kFlagIsReg | kFlagIsStack); }
     604             :   //! Tests whether the argument is passed through a pointer (used by WIN64 to pass XMM|YMM|ZMM).
     605             :   inline bool isIndirect() const noexcept { return hasFlag(kFlagIsIndirect); }
     606             : 
     607             :   //! Tests whether the argument was already processed (used internally).
     608             :   inline bool isDone() const noexcept { return hasFlag(kFlagIsDone); }
     609             : 
     610             :   //! Returns a register type of the register used to pass function argument or return value.
     611      103459 :   inline RegType regType() const noexcept { return RegType((_data & kRegTypeMask) >> kRegTypeShift); }
     612             :   //! Sets a register type of the register used to pass function argument or return value.
     613             :   inline void setRegType(RegType regType) noexcept { _replaceValue(kRegTypeMask, uint32_t(regType) << kRegTypeShift); }
     614             : 
     615             :   //! Returns a physical id of the register used to pass function argument or return value.
     616       47004 :   inline uint32_t regId() const noexcept { return (_data & kRegIdMask) >> kRegIdShift; }
     617             :   //! Sets a physical id of the register used to pass function argument or return value.
     618           0 :   inline void setRegId(uint32_t regId) noexcept { _replaceValue(kRegIdMask, regId << kRegIdShift); }
     619             : 
     620             :   //! Returns a stack offset of this argument.
     621           0 :   inline int32_t stackOffset() const noexcept { return int32_t(_data & kStackOffsetMask) >> kStackOffsetShift; }
     622             :   //! Sets a stack offset of this argument.
     623           0 :   inline void setStackOffset(int32_t offset) noexcept { _replaceValue(kStackOffsetMask, uint32_t(offset) << kStackOffsetShift); }
     624             : 
     625             :   //! Tests whether the argument or return value has associated `TypeId`.
     626           0 :   inline bool hasTypeId() const noexcept { return Support::test(_data, kTypeIdMask); }
     627             :   //! Returns a TypeId of this argument or return value.
     628      131327 :   inline TypeId typeId() const noexcept { return TypeId((_data & kTypeIdMask) >> kTypeIdShift); }
     629             :   //! Sets a TypeId of this argument or return value.
     630           0 :   inline void setTypeId(TypeId typeId) noexcept { _replaceValue(kTypeIdMask, uint32_t(typeId) << kTypeIdShift); }
     631             : 
     632             :   //! \}
     633             : };
     634             : 
     635             : //! Contains multiple `FuncValue` instances in an array so functions that use multiple registers for arguments or
     636             : //! return values can represent all inputs and outputs.
     637             : struct FuncValuePack {
     638             : public:
     639             :   //! \name Members
     640             :   //! \{
     641             : 
     642             :   //! Values of the pack.
     643             :   FuncValue _values[Globals::kMaxValuePack];
     644             : 
     645             :   //! \}
     646             : 
     647             :   //! \name Initialization & Reset
     648             :   //! \{
     649             : 
     650             :   //! Resets all values in the pack.
     651             :   inline void reset() noexcept {
     652             :     for (size_t i = 0; i < Globals::kMaxValuePack; i++)
     653             :       _values[i].reset();
     654             :   }
     655             : 
     656             :   //! \}
     657             : 
     658             :   //! \name Accessors
     659             :   //! \{
     660             : 
     661             :   //! Calculates how many values are in the pack, checking for non-values from the end.
     662             :   inline uint32_t count() const noexcept {
     663             :     uint32_t n = Globals::kMaxValuePack;
     664           0 :     while (n && !_values[n - 1])
     665             :       n--;
     666             :     return n;
     667             :   }
     668             : 
     669             :   inline FuncValue* values() noexcept { return _values; }
     670             :   inline const FuncValue* values() const noexcept { return _values; }
     671             : 
     672             :   inline void resetValue(size_t index) noexcept {
     673             :     ASMJIT_ASSERT(index < Globals::kMaxValuePack);
     674             :     _values[index].reset();
     675             :   }
     676             : 
     677             :   inline bool hasValue(size_t index) noexcept {
     678             :     ASMJIT_ASSERT(index < Globals::kMaxValuePack);
     679             :     return _values[index].isInitialized();
     680             :   }
     681             : 
     682             :   inline void assignReg(size_t index, const BaseReg& reg, TypeId typeId = TypeId::kVoid) noexcept {
     683             :     ASMJIT_ASSERT(index < Globals::kMaxValuePack);
     684             :     ASMJIT_ASSERT(reg.isPhysReg());
     685             :     _values[index].initReg(reg.type(), reg.id(), typeId);
     686             :   }
     687             : 
     688             :   inline void assignReg(size_t index, RegType regType, uint32_t regId, TypeId typeId = TypeId::kVoid) noexcept {
     689             :     ASMJIT_ASSERT(index < Globals::kMaxValuePack);
     690             :     _values[index].initReg(regType, regId, typeId);
     691             :   }
     692             : 
     693             :   inline void assignStack(size_t index, int32_t offset, TypeId typeId = TypeId::kVoid) noexcept {
     694             :     ASMJIT_ASSERT(index < Globals::kMaxValuePack);
     695             :     _values[index].initStack(offset, typeId);
     696             :   }
     697             : 
     698             :   inline FuncValue& operator[](size_t index) {
     699             :     ASMJIT_ASSERT(index < Globals::kMaxValuePack);
     700       56877 :     return _values[index];
     701             :   }
     702             : 
     703             :   inline const FuncValue& operator[](size_t index) const {
     704             :     ASMJIT_ASSERT(index < Globals::kMaxValuePack);
     705        9873 :     return _values[index];
     706             :   }
     707             : 
     708             :   //! \}
     709             : };
     710             : 
     711             : //! Attributes are designed in a way that all are initially false, and user or \ref FuncFrame finalizer adds
     712             : //! them when necessary.
     713             : enum class FuncAttributes : uint32_t {
     714             :   //! No attributes.
     715             :   kNoAttributes = 0,
     716             : 
     717             :   //! Function has variable number of arguments.
     718             :   kHasVarArgs = 0x00000001u,
     719             :   //! Preserve frame pointer (don't omit FP).
     720             :   kHasPreservedFP = 0x00000010u,
     721             :   //! Function calls other functions (is not leaf).
     722             :   kHasFuncCalls = 0x00000020u,
     723             :   //! Function has aligned save/restore of vector registers.
     724             :   kAlignedVecSR = 0x00000040u,
     725             :   //! FuncFrame is finalized and can be used by prolog/epilog inserter (PEI).
     726             :   kIsFinalized = 0x00000800u,
     727             : 
     728             :   // X86 Specific Attributes
     729             :   // -----------------------
     730             : 
     731             :   //! Enables the use of AVX within the function's body, prolog, and epilog (X86).
     732             :   //!
     733             :   //! This flag instructs prolog and epilog emitter to use AVX instead of SSE for manipulating XMM registers.
     734             :   kX86_AVXEnabled = 0x00010000u,
     735             : 
     736             :   //! Enables the use of AVX-512 within the function's body, prolog, and epilog (X86).
     737             :   //!
     738             :   //! This flag instructs Compiler register allocator to use additional 16 registers introduced by AVX-512.
     739             :   //! Additionally, if the functions saves full width of ZMM registers (custom calling conventions only) then
     740             :   //! the prolog/epilog inserter would use AVX-512 move instructions to emit the save and restore sequence.
     741             :   kX86_AVX512Enabled = 0x00020000u,
     742             : 
     743             :   //! This flag instructs the epilog writer to emit EMMS instruction before RET (X86).
     744             :   kX86_MMXCleanup = 0x00040000u,
     745             : 
     746             :   //! This flag instructs the epilog writer to emit VZEROUPPER instruction before RET (X86).
     747             :   kX86_AVXCleanup = 0x00080000u
     748             : };
     749       37131 : ASMJIT_DEFINE_ENUM_FLAGS(FuncAttributes)
     750             : 
     751             : //! Function detail - \ref CallConv and expanded \ref FuncSignature.
     752             : //!
     753             : //! Function detail is architecture and OS dependent representation of a function. It contains a materialized
     754             : //! calling convention and expanded function signature so all arguments have assigned either register type/id
     755             : //! or stack address.
     756             : class FuncDetail {
     757             : public:
     758             :   //! \name Constants
     759             :   //! \{
     760             : 
     761             :   enum : uint8_t {
     762             :     //! Doesn't have variable number of arguments (`...`).
     763             :     kNoVarArgs = 0xFFu
     764             :   };
     765             : 
     766             :   //! \}
     767             : 
     768             :   //! \name Members
     769             :   //! \{
     770             : 
     771             :   //! Calling convention.
     772             :   CallConv _callConv;
     773             :   //! Number of function arguments.
     774             :   uint8_t _argCount;
     775             :   //! Variable arguments index of `kNoVarArgs`.
     776             :   uint8_t _vaIndex;
     777             :   //! Reserved for future use.
     778             :   uint16_t _reserved;
     779             :   //! Registers that contain arguments.
     780             :   Support::Array<RegMask, Globals::kNumVirtGroups> _usedRegs;
     781             :   //! Size of arguments passed by stack.
     782             :   uint32_t _argStackSize;
     783             :   //! Function return value(s).
     784             :   FuncValuePack _rets;
     785             :   //! Function arguments.
     786             :   FuncValuePack _args[Globals::kMaxFuncArgs];
     787             : 
     788             :   //! \}
     789             : 
     790             :   //! \name Construction & Destruction
     791             :   //! \{
     792             : 
     793       37131 :   inline FuncDetail() noexcept { reset(); }
     794             :   inline FuncDetail(const FuncDetail& other) noexcept = default;
     795             : 
     796             :   //! Initializes this `FuncDetail` to the given signature.
     797             :   ASMJIT_API Error init(const FuncSignature& signature, const Environment& environment) noexcept;
     798       37131 :   inline void reset() noexcept { memset(this, 0, sizeof(*this)); }
     799             : 
     800             :   //! \}
     801             : 
     802             :   //! \name Accessors
     803             :   //! \{
     804             : 
     805             :   //! Returns the function's calling convention, see `CallConv`.
     806             :   inline const CallConv& callConv() const noexcept { return _callConv; }
     807             : 
     808             :   //! Returns the associated calling convention flags, see `CallConv::Flags`.
     809             :   inline CallConvFlags flags() const noexcept { return _callConv.flags(); }
     810             :   //! Checks whether a CallConv `flag` is set, see `CallConv::Flags`.
     811             :   inline bool hasFlag(CallConvFlags ccFlag) const noexcept { return _callConv.hasFlag(ccFlag); }
     812             : 
     813             :   //! Tests whether the function has a return value.
     814             :   inline bool hasRet() const noexcept { return bool(_rets[0]); }
     815             :   //! Returns the number of function arguments.
     816      138885 :   inline uint32_t argCount() const noexcept { return _argCount; }
     817             : 
     818             :   //! Returns function return values.
     819             :   inline FuncValuePack& retPack() noexcept { return _rets; }
     820             :   //! Returns function return values.
     821           0 :   inline const FuncValuePack& retPack() const noexcept { return _rets; }
     822             : 
     823             :   //! Returns a function return value associated with the given `valueIndex`.
     824             :   inline FuncValue& ret(size_t valueIndex = 0) noexcept { return _rets[valueIndex]; }
     825             :   //! Returns a function return value associated with the given `valueIndex` (const).
     826             :   inline const FuncValue& ret(size_t valueIndex = 0) const noexcept { return _rets[valueIndex]; }
     827             : 
     828             :   //! Returns function argument packs array.
     829             :   inline FuncValuePack* argPacks() noexcept { return _args; }
     830             :   //! Returns function argument packs array (const).
     831             :   inline const FuncValuePack* argPacks() const noexcept { return _args; }
     832             : 
     833             :   //! Returns function argument pack at the given `argIndex`.
     834             :   inline FuncValuePack& argPack(size_t argIndex) noexcept {
     835             :     ASMJIT_ASSERT(argIndex < Globals::kMaxFuncArgs);
     836             :     return _args[argIndex];
     837             :   }
     838             : 
     839             :   //! Returns function argument pack at the given `argIndex` (const).
     840             :   inline const FuncValuePack& argPack(size_t argIndex) const noexcept {
     841             :     ASMJIT_ASSERT(argIndex < Globals::kMaxFuncArgs);
     842           0 :     return _args[argIndex];
     843             :   }
     844             : 
     845             :   //! Returns an argument at `valueIndex` from the argument pack at the given `argIndex`.
     846             :   inline FuncValue& arg(size_t argIndex, size_t valueIndex = 0) noexcept {
     847             :     ASMJIT_ASSERT(argIndex < Globals::kMaxFuncArgs);
     848             :     return _args[argIndex][valueIndex];
     849             :   }
     850             : 
     851             :   //! Returns an argument at `valueIndex` from the argument pack at the given `argIndex` (const).
     852             :   inline const FuncValue& arg(size_t argIndex, size_t valueIndex = 0) const noexcept {
     853             :     ASMJIT_ASSERT(argIndex < Globals::kMaxFuncArgs);
     854             :     return _args[argIndex][valueIndex];
     855             :   }
     856             : 
     857             :   //! Resets an argument at the given `argIndex`.
     858             :   //!
     859             :   //! If the argument is a parameter pack (has multiple values) all values are reset.
     860             :   inline void resetArg(size_t argIndex) noexcept {
     861             :     ASMJIT_ASSERT(argIndex < Globals::kMaxFuncArgs);
     862             :     _args[argIndex].reset();
     863             :   }
     864             : 
     865             :   //! Tests whether the function has variable arguments.
     866        9639 :   inline bool hasVarArgs() const noexcept { return _vaIndex != kNoVarArgs; }
     867             :   //! Returns an index of a first variable argument.
     868             :   inline uint32_t vaIndex() const noexcept { return _vaIndex; }
     869             : 
     870             :   //! Tests whether the function passes one or more argument by stack.
     871             :   inline bool hasStackArgs() const noexcept { return _argStackSize != 0; }
     872             :   //! Returns stack size needed for function arguments passed on the stack.
     873        9639 :   inline uint32_t argStackSize() const noexcept { return _argStackSize; }
     874             : 
     875             :   //! Returns red zone size.
     876             :   inline uint32_t redZoneSize() const noexcept { return _callConv.redZoneSize(); }
     877             :   //! Returns spill zone size.
     878             :   inline uint32_t spillZoneSize() const noexcept { return _callConv.spillZoneSize(); }
     879             :   //! Returns natural stack alignment.
     880             :   inline uint32_t naturalStackAlignment() const noexcept { return _callConv.naturalStackAlignment(); }
     881             : 
     882             :   //! Returns a mask of all passed registers of the given register `group`.
     883             :   inline RegMask passedRegs(RegGroup group) const noexcept { return _callConv.passedRegs(group); }
     884             :   //! Returns a mask of all preserved registers of the given register `group`.
     885             :   inline RegMask preservedRegs(RegGroup group) const noexcept { return _callConv.preservedRegs(group); }
     886             : 
     887             :   //! Returns a mask of all used registers of the given register `group`.
     888             :   inline RegMask usedRegs(RegGroup group) const noexcept {
     889             :     ASMJIT_ASSERT(group <= RegGroup::kMaxVirt);
     890      109968 :     return _usedRegs[size_t(group)];
     891             :   }
     892             : 
     893             :   //! Adds `regs` to the mask of used registers of the given register `group`.
     894             :   inline void addUsedRegs(RegGroup group, RegMask regs) noexcept {
     895             :     ASMJIT_ASSERT(group <= RegGroup::kMaxVirt);
     896        9873 :     _usedRegs[size_t(group)] |= regs;
     897           0 :   }
     898             : 
     899             :   //! \}
     900             : };
     901             : 
     902             : //! Function frame.
     903             : //!
     904             : //! Function frame is used directly by prolog and epilog insertion (PEI) utils. It provides information necessary to
     905             : //! insert a proper and ABI comforming prolog and epilog. Function frame calculation is based on `CallConv` and
     906             : //! other function attributes.
     907             : //!
     908             : //! SSE vs AVX vs AVX-512
     909             : //! ---------------------
     910             : //!
     911             : //! Function frame provides a way to tell prolog/epilog inserter to use AVX instructions instead of SSE. Use
     912             : //! `setAvxEnabled()` and `setAvx512Enabled()`  to enable AVX and/or AVX-512, respectively. Enabling AVX-512
     913             : //! is mostly for Compiler as it would use 32 SIMD registers instead of 16 when enabled.
     914             : //!
     915             : //! \note If your code uses AVX instructions and AVX is not enabled there would be a performance hit in case that
     916             : //! some registers had to be saved/restored in function's prolog/epilog, respectively. Thus, it's recommended to
     917             : //! always let the function frame know about the use of AVX.
     918             : //!
     919             : //! Function Frame Structure
     920             : //! ------------------------
     921             : //!
     922             : //! Various properties can contribute to the size and structure of the function frame. The function frame in most
     923             : //! cases won't use all of the properties illustrated (for example Spill Zone and Red Zone are never used together).
     924             : //!
     925             : //! ```
     926             : //!   +-----------------------------+
     927             : //!   | Arguments Passed by Stack   |
     928             : //!   +-----------------------------+
     929             : //!   | Spill Zone                  |
     930             : //!   +-----------------------------+ <- Stack offset (args) starts from here.
     931             : //!   | Return Address, if Pushed   |
     932             : //!   +-----------------------------+ <- Stack pointer (SP) upon entry.
     933             : //!   | Save/Restore Stack.         |
     934             : //!   +-----------------------------+-----------------------------+
     935             : //!   | Local Stack                 |                             |
     936             : //!   +-----------------------------+          Final Stack        |
     937             : //!   | Call Stack                  |                             |
     938             : //!   +-----------------------------+-----------------------------+ <- SP after prolog.
     939             : //!   | Red Zone                    |
     940             : //!   +-----------------------------+
     941             : //! ```
     942             : class FuncFrame {
     943             : public:
     944             :   //! \name Constants
     945             :   //! \{
     946             : 
     947             :   enum : uint32_t {
     948             :     //! Tag used to inform that some offset is invalid.
     949             :     kTagInvalidOffset = 0xFFFFFFFFu
     950             :   };
     951             : 
     952             :   //! \}
     953             : 
     954             :   //! \name Members
     955             :   //! \{
     956             : 
     957             :   //! Function attributes.
     958             :   FuncAttributes _attributes;
     959             : 
     960             :   //! Target architecture.
     961             :   Arch _arch;
     962             :   //! SP register ID (to access call stack and local stack).
     963             :   uint8_t _spRegId;
     964             :   //! SA register ID (to access stack arguments).
     965             :   uint8_t _saRegId;
     966             : 
     967             :   //! Red zone size (copied from CallConv).
     968             :   uint8_t _redZoneSize;
     969             :   //! Spill zone size (copied from CallConv).
     970             :   uint8_t _spillZoneSize;
     971             :   //! Natural stack alignment (copied from CallConv).
     972             :   uint8_t _naturalStackAlignment;
     973             :   //! Minimum stack alignment to turn on dynamic alignment.
     974             :   uint8_t _minDynamicAlignment;
     975             : 
     976             :   //! Call stack alignment.
     977             :   uint8_t _callStackAlignment;
     978             :   //! Local stack alignment.
     979             :   uint8_t _localStackAlignment;
     980             :   //! Final stack alignment.
     981             :   uint8_t _finalStackAlignment;
     982             : 
     983             :   //! Adjustment of the stack before returning (X86-STDCALL).
     984             :   uint16_t _calleeStackCleanup;
     985             : 
     986             :   //! Call stack size.
     987             :   uint32_t _callStackSize;
     988             :   //! Local stack size.
     989             :   uint32_t _localStackSize;
     990             :   //! Final stack size (sum of call stack and local stack).
     991             :   uint32_t _finalStackSize;
     992             : 
     993             :   //! Local stack offset (non-zero only if call stack is used).
     994             :   uint32_t _localStackOffset;
     995             :   //! Offset relative to SP that contains previous SP (before alignment).
     996             :   uint32_t _daOffset;
     997             :   //! Offset of the first stack argument relative to SP.
     998             :   uint32_t _saOffsetFromSP;
     999             :   //! Offset of the first stack argument relative to SA (_saRegId or FP).
    1000             :   uint32_t _saOffsetFromSA;
    1001             : 
    1002             :   //! Local stack adjustment in prolog/epilog.
    1003             :   uint32_t _stackAdjustment;
    1004             : 
    1005             :   //! Registers that are dirty.
    1006             :   Support::Array<RegMask, Globals::kNumVirtGroups> _dirtyRegs;
    1007             :   //! Registers that must be preserved (copied from CallConv).
    1008             :   Support::Array<RegMask, Globals::kNumVirtGroups> _preservedRegs;
    1009             :   //! Size to save/restore per register group.
    1010             :   Support::Array<uint8_t, Globals::kNumVirtGroups> _saveRestoreRegSize;
    1011             :   //! Alignment of save/restore area per register group.
    1012             :   Support::Array<uint8_t, Globals::kNumVirtGroups> _saveRestoreAlignment;
    1013             : 
    1014             :   //! Stack size required to save registers with push/pop.
    1015             :   uint16_t _pushPopSaveSize;
    1016             :   //! Stack size required to save extra registers that cannot use push/pop.
    1017             :   uint16_t _extraRegSaveSize;
    1018             :   //! Offset where registers saved/restored via push/pop are stored
    1019             :   uint32_t _pushPopSaveOffset;
    1020             :   //! Offset where extra ragisters that cannot use push/pop are stored.
    1021             :   uint32_t _extraRegSaveOffset;
    1022             : 
    1023             :   //! \}
    1024             : 
    1025             :   //! \name Construction & Destruction
    1026             :   //! \{
    1027             : 
    1028       27492 :   inline FuncFrame() noexcept { reset(); }
    1029             :   inline FuncFrame(const FuncFrame& other) noexcept = default;
    1030             : 
    1031             :   ASMJIT_API Error init(const FuncDetail& func) noexcept;
    1032             : 
    1033       54984 :   inline void reset() noexcept {
    1034             :     memset(this, 0, sizeof(FuncFrame));
    1035       54984 :     _spRegId = BaseReg::kIdBad;
    1036       54984 :     _saRegId = BaseReg::kIdBad;
    1037       54984 :     _daOffset = kTagInvalidOffset;
    1038       54984 :   }
    1039             : 
    1040             :   //! \}
    1041             : 
    1042             :   //! \name Accessors
    1043             :   //! \{
    1044             : 
    1045             :   //! Returns the target architecture of the function frame.
    1046      109968 :   inline Arch arch() const noexcept { return _arch; }
    1047             : 
    1048             :   //! Returns function frame attributes, see `Attributes`.
    1049             :   inline FuncAttributes attributes() const noexcept { return _attributes; }
    1050             :   //! Checks whether the FuncFame contains an attribute `attr`.
    1051      326259 :   inline bool hasAttribute(FuncAttributes attr) const noexcept { return Support::test(_attributes, attr); }
    1052             :   //! Adds attributes `attrs` to the FuncFrame.
    1053             :   inline void addAttributes(FuncAttributes attrs) noexcept { _attributes |= attrs; }
    1054             :   //! Clears attributes `attrs` from the FrameFrame.
    1055             :   inline void clearAttributes(FuncAttributes attrs) noexcept { _attributes &= ~attrs; }
    1056             : 
    1057             :   //! Tests whether the function has variable number of arguments.
    1058             :   inline bool hasVarArgs() const noexcept { return hasAttribute(FuncAttributes::kHasVarArgs); }
    1059             :   //! Sets the variable arguments flag.
    1060             :   inline void setVarArgs() noexcept { addAttributes(FuncAttributes::kHasVarArgs); }
    1061             :   //! Resets variable arguments flag.
    1062             :   inline void resetVarArgs() noexcept { clearAttributes(FuncAttributes::kHasVarArgs); }
    1063             : 
    1064             :   //! Tests whether the function preserves frame pointer (EBP|ESP on X86).
    1065             :   inline bool hasPreservedFP() const noexcept { return hasAttribute(FuncAttributes::kHasPreservedFP); }
    1066             :   //! Enables preserved frame pointer.
    1067             :   inline void setPreservedFP() noexcept { addAttributes(FuncAttributes::kHasPreservedFP); }
    1068             :   //! Disables preserved frame pointer.
    1069             :   inline void resetPreservedFP() noexcept { clearAttributes(FuncAttributes::kHasPreservedFP); }
    1070             : 
    1071             :   //! Tests whether the function calls other functions.
    1072             :   inline bool hasFuncCalls() const noexcept { return hasAttribute(FuncAttributes::kHasFuncCalls); }
    1073             :   //! Sets `kFlagHasCalls` to true.
    1074             :   inline void setFuncCalls() noexcept { addAttributes(FuncAttributes::kHasFuncCalls); }
    1075             :   //! Sets `kFlagHasCalls` to false.
    1076             :   inline void resetFuncCalls() noexcept { clearAttributes(FuncAttributes::kHasFuncCalls); }
    1077             : 
    1078             :   //! Tests whether the function has AVX enabled.
    1079             :   inline bool isAvxEnabled() const noexcept { return hasAttribute(FuncAttributes::kX86_AVXEnabled); }
    1080             :   //! Enables AVX use.
    1081             :   inline void setAvxEnabled() noexcept { addAttributes(FuncAttributes::kX86_AVXEnabled); }
    1082             :   //! Disables AVX use.
    1083             :   inline void resetAvxEnabled() noexcept { clearAttributes(FuncAttributes::kX86_AVXEnabled); }
    1084             : 
    1085             :   //! Tests whether the function has AVX-512 enabled.
    1086             :   inline bool isAvx512Enabled() const noexcept { return hasAttribute(FuncAttributes::kX86_AVX512Enabled); }
    1087             :   //! Enables AVX-512 use.
    1088             :   inline void setAvx512Enabled() noexcept { addAttributes(FuncAttributes::kX86_AVX512Enabled); }
    1089             :   //! Disables AVX-512 use.
    1090             :   inline void resetAvx512Enabled() noexcept { clearAttributes(FuncAttributes::kX86_AVX512Enabled); }
    1091             : 
    1092             :   //! Tests whether the function has MMX cleanup - 'emms' instruction in epilog.
    1093             :   inline bool hasMmxCleanup() const noexcept { return hasAttribute(FuncAttributes::kX86_MMXCleanup); }
    1094             :   //! Enables MMX cleanup.
    1095             :   inline void setMmxCleanup() noexcept { addAttributes(FuncAttributes::kX86_MMXCleanup); }
    1096             :   //! Disables MMX cleanup.
    1097             :   inline void resetMmxCleanup() noexcept { clearAttributes(FuncAttributes::kX86_MMXCleanup); }
    1098             : 
    1099             :   //! Tests whether the function has AVX cleanup - 'vzeroupper' instruction in epilog.
    1100             :   inline bool hasAvxCleanup() const noexcept { return hasAttribute(FuncAttributes::kX86_AVXCleanup); }
    1101             :   //! Enables AVX cleanup.
    1102             :   inline void setAvxCleanup() noexcept { addAttributes(FuncAttributes::kX86_AVXCleanup); }
    1103             :   //! Disables AVX cleanup.
    1104             :   inline void resetAvxCleanup() noexcept { clearAttributes(FuncAttributes::kX86_AVXCleanup); }
    1105             : 
    1106             :   //! Tests whether the function uses call stack.
    1107             :   inline bool hasCallStack() const noexcept { return _callStackSize != 0; }
    1108             :   //! Tests whether the function uses local stack.
    1109             :   inline bool hasLocalStack() const noexcept { return _localStackSize != 0; }
    1110             :   //! Tests whether vector registers can be saved and restored by using aligned reads and writes.
    1111             :   inline bool hasAlignedVecSR() const noexcept { return hasAttribute(FuncAttributes::kAlignedVecSR); }
    1112             :   //! Tests whether the function has to align stack dynamically.
    1113      137460 :   inline bool hasDynamicAlignment() const noexcept { return _finalStackAlignment >= _minDynamicAlignment; }
    1114             : 
    1115             :   //! Tests whether the calling convention specifies 'RedZone'.
    1116             :   inline bool hasRedZone() const noexcept { return _redZoneSize != 0; }
    1117             :   //! Tests whether the calling convention specifies 'SpillZone'.
    1118             :   inline bool hasSpillZone() const noexcept { return _spillZoneSize != 0; }
    1119             : 
    1120             :   //! Returns the size of 'RedZone'.
    1121             :   inline uint32_t redZoneSize() const noexcept { return _redZoneSize; }
    1122             :   //! Returns the size of 'SpillZone'.
    1123             :   inline uint32_t spillZoneSize() const noexcept { return _spillZoneSize; }
    1124             :   //! Returns natural stack alignment (guaranteed stack alignment upon entry).
    1125             :   inline uint32_t naturalStackAlignment() const noexcept { return _naturalStackAlignment; }
    1126             :   //! Returns natural stack alignment (guaranteed stack alignment upon entry).
    1127             :   inline uint32_t minDynamicAlignment() const noexcept { return _minDynamicAlignment; }
    1128             : 
    1129             :   //! Tests whether the callee must adjust SP before returning (X86-STDCALL only)
    1130       27492 :   inline bool hasCalleeStackCleanup() const noexcept { return _calleeStackCleanup != 0; }
    1131             :   //! Returns home many bytes of the stack the callee must adjust before returning (X86-STDCALL only)
    1132             :   inline uint32_t calleeStackCleanup() const noexcept { return _calleeStackCleanup; }
    1133             : 
    1134             :   //! Returns call stack alignment.
    1135             :   inline uint32_t callStackAlignment() const noexcept { return _callStackAlignment; }
    1136             :   //! Returns local stack alignment.
    1137             :   inline uint32_t localStackAlignment() const noexcept { return _localStackAlignment; }
    1138             :   //! Returns final stack alignment (the maximum value of call, local, and natural stack alignments).
    1139             :   inline uint32_t finalStackAlignment() const noexcept { return _finalStackAlignment; }
    1140             : 
    1141             :   //! Sets call stack alignment.
    1142             :   //!
    1143             :   //! \note This also updates the final stack alignment.
    1144             :   inline void setCallStackAlignment(uint32_t alignment) noexcept {
    1145             :     _callStackAlignment = uint8_t(alignment);
    1146             :     _finalStackAlignment = Support::max(_naturalStackAlignment, _callStackAlignment, _localStackAlignment);
    1147             :   }
    1148             : 
    1149             :   //! Sets local stack alignment.
    1150             :   //!
    1151             :   //! \note This also updates the final stack alignment.
    1152             :   inline void setLocalStackAlignment(uint32_t value) noexcept {
    1153       27492 :     _localStackAlignment = uint8_t(value);
    1154       27492 :     _finalStackAlignment = Support::max(_naturalStackAlignment, _callStackAlignment, _localStackAlignment);
    1155             :   }
    1156             : 
    1157             :   //! Combines call stack alignment with `alignment`, updating it to the greater value.
    1158             :   //!
    1159             :   //! \note This also updates the final stack alignment.
    1160             :   inline void updateCallStackAlignment(uint32_t alignment) noexcept {
    1161           0 :     _callStackAlignment = uint8_t(Support::max<uint32_t>(_callStackAlignment, alignment));
    1162           0 :     _finalStackAlignment = Support::max(_finalStackAlignment, _callStackAlignment);
    1163             :   }
    1164             : 
    1165             :   //! Combines local stack alignment with `alignment`, updating it to the greater value.
    1166             :   //!
    1167             :   //! \note This also updates the final stack alignment.
    1168             :   inline void updateLocalStackAlignment(uint32_t alignment) noexcept {
    1169             :     _localStackAlignment = uint8_t(Support::max<uint32_t>(_localStackAlignment, alignment));
    1170             :     _finalStackAlignment = Support::max(_finalStackAlignment, _localStackAlignment);
    1171             :   }
    1172             : 
    1173             :   //! Returns call stack size.
    1174       27492 :   inline uint32_t callStackSize() const noexcept { return _callStackSize; }
    1175             :   //! Returns local stack size.
    1176       27492 :   inline uint32_t localStackSize() const noexcept { return _localStackSize; }
    1177             : 
    1178             :   //! Sets call stack size.
    1179             :   inline void setCallStackSize(uint32_t size) noexcept { _callStackSize = size; }
    1180             :   //! Sets local stack size.
    1181       27492 :   inline void setLocalStackSize(uint32_t size) noexcept { _localStackSize = size; }
    1182             : 
    1183             :   //! Combines call stack size with `size`, updating it to the greater value.
    1184        9639 :   inline void updateCallStackSize(uint32_t size) noexcept { _callStackSize = Support::max(_callStackSize, size); }
    1185             :   //! Combines local stack size with `size`, updating it to the greater value.
    1186             :   inline void updateLocalStackSize(uint32_t size) noexcept { _localStackSize = Support::max(_localStackSize, size); }
    1187             : 
    1188             :   //! Returns final stack size (only valid after the FuncFrame is finalized).
    1189             :   inline uint32_t finalStackSize() const noexcept { return _finalStackSize; }
    1190             : 
    1191             :   //! Returns an offset to access the local stack (non-zero only if call stack is used).
    1192       27492 :   inline uint32_t localStackOffset() const noexcept { return _localStackOffset; }
    1193             : 
    1194             :   //! Tests whether the function prolog/epilog requires a memory slot for storing unaligned SP.
    1195           0 :   inline bool hasDAOffset() const noexcept { return _daOffset != kTagInvalidOffset; }
    1196             :   //! Returns a memory offset used to store DA (dynamic alignment) slot (relative to SP).
    1197             :   inline uint32_t daOffset() const noexcept { return _daOffset; }
    1198             : 
    1199             :   inline uint32_t saOffset(uint32_t regId) const noexcept {
    1200           0 :     return regId == _spRegId ? saOffsetFromSP()
    1201             :                              : saOffsetFromSA();
    1202             :   }
    1203             : 
    1204           0 :   inline uint32_t saOffsetFromSP() const noexcept { return _saOffsetFromSP; }
    1205           0 :   inline uint32_t saOffsetFromSA() const noexcept { return _saOffsetFromSA; }
    1206             : 
    1207             :   //! Returns mask of registers of the given register `group` that are modified by the function. The engine would
    1208             :   //! then calculate which registers must be saved & restored by the function by using the data provided by the
    1209             :   //! calling convention.
    1210             :   inline RegMask dirtyRegs(RegGroup group) const noexcept {
    1211             :     ASMJIT_ASSERT(group <= RegGroup::kMaxVirt);
    1212      219936 :     return _dirtyRegs[group];
    1213             :   }
    1214             : 
    1215             :   //! Sets which registers (as a mask) are modified by the function.
    1216             :   //!
    1217             :   //! \remarks Please note that this will completely overwrite the existing register mask, use `addDirtyRegs()`
    1218             :   //! to modify the existing register mask.
    1219             :   inline void setDirtyRegs(RegGroup group, RegMask regs) noexcept {
    1220             :     ASMJIT_ASSERT(group <= RegGroup::kMaxVirt);
    1221             :     _dirtyRegs[group] = regs;
    1222             :   }
    1223             : 
    1224             :   //! Adds which registers (as a mask) are modified by the function.
    1225             :   inline void addDirtyRegs(RegGroup group, RegMask regs) noexcept {
    1226             :     ASMJIT_ASSERT(group <= RegGroup::kMaxVirt);
    1227      219936 :     _dirtyRegs[group] |= regs;
    1228           0 :   }
    1229             : 
    1230             :   //! \overload
    1231             :   inline void addDirtyRegs(const BaseReg& reg) noexcept {
    1232             :     ASMJIT_ASSERT(reg.id() < Globals::kMaxPhysRegs);
    1233             :     addDirtyRegs(reg.group(), Support::bitMask(reg.id()));
    1234             :   }
    1235             : 
    1236             :   //! \overload
    1237             :   template<typename... Args>
    1238             :   inline void addDirtyRegs(const BaseReg& reg, Args&&... args) noexcept {
    1239             :     addDirtyRegs(reg);
    1240             :     addDirtyRegs(std::forward<Args>(args)...);
    1241             :   }
    1242             : 
    1243             :   inline void setAllDirty() noexcept {
    1244             :     for (size_t i = 0; i < ASMJIT_ARRAY_SIZE(_dirtyRegs); i++)
    1245             :       _dirtyRegs[i] = 0xFFFFFFFFu;
    1246             :   }
    1247             : 
    1248             :   inline void setAllDirty(RegGroup group) noexcept {
    1249             :     ASMJIT_ASSERT(group <= RegGroup::kMaxVirt);
    1250             :     _dirtyRegs[group] = 0xFFFFFFFFu;
    1251             :   }
    1252             : 
    1253             :   //! Returns a calculated mask of registers of the given `group` that will be saved and restored in the function's
    1254             :   //! prolog and epilog, respectively. The register mask is calculated from both `dirtyRegs` (provided by user) and
    1255             :   //! `preservedMask` (provided by the calling convention).
    1256             :   inline RegMask savedRegs(RegGroup group) const noexcept {
    1257             :     ASMJIT_ASSERT(group <= RegGroup::kMaxVirt);
    1258      329904 :     return _dirtyRegs[group] & _preservedRegs[group];
    1259             :   }
    1260             : 
    1261             :   //! Returns the mask of preserved registers of the given register `group`.
    1262             :   //!
    1263             :   //! Preserved registers are those that must survive the function call unmodified. The function can only modify
    1264             :   //! preserved registers it they are saved and restored in funciton's prolog and epilog, respectively.
    1265             :   inline RegMask preservedRegs(RegGroup group) const noexcept {
    1266             :     ASMJIT_ASSERT(group <= RegGroup::kMaxVirt);
    1267      219936 :     return _preservedRegs[group];
    1268             :   }
    1269             : 
    1270             :   inline uint32_t saveRestoreRegSize(RegGroup group) const noexcept {
    1271             :     ASMJIT_ASSERT(group <= RegGroup::kMaxVirt);
    1272      109968 :     return _saveRestoreRegSize[group];
    1273             :   }
    1274             : 
    1275             :   inline uint32_t saveRestoreAlignment(RegGroup group) const noexcept {
    1276             :     ASMJIT_ASSERT(group <= RegGroup::kMaxVirt);
    1277      109968 :     return _saveRestoreAlignment[group];
    1278             :   }
    1279             : 
    1280             :   inline bool hasSARegId() const noexcept { return _saRegId != BaseReg::kIdBad; }
    1281       82476 :   inline uint32_t saRegId() const noexcept { return _saRegId; }
    1282           0 :   inline void setSARegId(uint32_t regId) { _saRegId = uint8_t(regId); }
    1283             :   inline void resetSARegId() { setSARegId(BaseReg::kIdBad); }
    1284             : 
    1285             :   //! Returns stack size required to save/restore registers via push/pop.
    1286        4990 :   inline uint32_t pushPopSaveSize() const noexcept { return _pushPopSaveSize; }
    1287             :   //! Returns an offset to the stack where registers are saved via push/pop.
    1288             :   inline uint32_t pushPopSaveOffset() const noexcept { return _pushPopSaveOffset; }
    1289             : 
    1290             :   //! Returns stack size required to save/restore extra registers that don't use push/pop/
    1291             :   //!
    1292             :   //! \note On X86 this covers all registers except GP registers, on other architectures it can be always
    1293             :   //! zero (for example AArch64 saves all registers via push/pop like instructions, so this would be zero).
    1294             :   inline uint32_t extraRegSaveSize() const noexcept { return _extraRegSaveSize; }
    1295             :   //! Returns an offset to the stack where extra registers are saved.
    1296       54984 :   inline uint32_t extraRegSaveOffset() const noexcept { return _extraRegSaveOffset; }
    1297             : 
    1298             :   //! Tests whether the functions contains stack adjustment.
    1299       54984 :   inline bool hasStackAdjustment() const noexcept { return _stackAdjustment != 0; }
    1300             :   //! Returns function's stack adjustment used in function's prolog and epilog.
    1301             :   //!
    1302             :   //! If the returned value is zero it means that the stack is not adjusted. This can mean both that the stack
    1303             :   //! is not used and/or the stack is only adjusted by instructions that pust/pop registers into/from stack.
    1304             :   inline uint32_t stackAdjustment() const noexcept { return _stackAdjustment; }
    1305             : 
    1306             :   //! \}
    1307             : 
    1308             :   //! \name Finaliztion
    1309             :   //! \{
    1310             : 
    1311             :   ASMJIT_API Error finalize() noexcept;
    1312             : 
    1313             :   //! \}
    1314             : };
    1315             : 
    1316             : //! A helper class that can be used to assign a physical register for each function argument. Use with
    1317             : //! `BaseEmitter::emitArgsAssignment()`.
    1318             : class FuncArgsAssignment {
    1319             : public:
    1320             :   //! \name Members
    1321             :   //! \{
    1322             : 
    1323             :   //! Function detail.
    1324             :   const FuncDetail* _funcDetail;
    1325             :   //! Register that can be used to access arguments passed by stack.
    1326             :   uint8_t _saRegId;
    1327             :   //! Reserved for future use.
    1328             :   uint8_t _reserved[3];
    1329             :   //! Mapping of each function argument.
    1330             :   FuncValuePack _argPacks[Globals::kMaxFuncArgs];
    1331             : 
    1332             :   //! \}
    1333             : 
    1334             :   //! \name Construction & Destruction
    1335             :   //! \{
    1336             : 
    1337       27492 :   inline explicit FuncArgsAssignment(const FuncDetail* fd = nullptr) noexcept { reset(fd); }
    1338             : 
    1339             :   inline FuncArgsAssignment(const FuncArgsAssignment& other) noexcept {
    1340             :     memcpy(this, &other, sizeof(*this));
    1341             :   }
    1342             : 
    1343       82476 :   inline void reset(const FuncDetail* fd = nullptr) noexcept {
    1344       82476 :     _funcDetail = fd;
    1345       82476 :     _saRegId = uint8_t(BaseReg::kIdBad);
    1346       82476 :     memset(_reserved, 0, sizeof(_reserved));
    1347       82476 :     memset(_argPacks, 0, sizeof(_argPacks));
    1348       82476 :   }
    1349             : 
    1350             :   //! \}
    1351             : 
    1352             :   //! \name Accessors
    1353             :   //! \{
    1354             : 
    1355       82476 :   inline const FuncDetail* funcDetail() const noexcept { return _funcDetail; }
    1356             :   inline void setFuncDetail(const FuncDetail* fd) noexcept { _funcDetail = fd; }
    1357             : 
    1358             :   inline bool hasSARegId() const noexcept { return _saRegId != BaseReg::kIdBad; }
    1359       54984 :   inline uint32_t saRegId() const noexcept { return _saRegId; }
    1360             :   inline void setSARegId(uint32_t regId) { _saRegId = uint8_t(regId); }
    1361             :   inline void resetSARegId() { _saRegId = uint8_t(BaseReg::kIdBad); }
    1362             : 
    1363             :   inline FuncValue& arg(size_t argIndex, size_t valueIndex) noexcept {
    1364             :     ASMJIT_ASSERT(argIndex < ASMJIT_ARRAY_SIZE(_argPacks));
    1365             :     return _argPacks[argIndex][valueIndex];
    1366             :   }
    1367             :   inline const FuncValue& arg(size_t argIndex, size_t valueIndex) const noexcept {
    1368             :     ASMJIT_ASSERT(argIndex < ASMJIT_ARRAY_SIZE(_argPacks));
    1369             :     return _argPacks[argIndex][valueIndex];
    1370             :   }
    1371             : 
    1372             :   inline bool isAssigned(size_t argIndex, size_t valueIndex) const noexcept {
    1373             :     ASMJIT_ASSERT(argIndex < ASMJIT_ARRAY_SIZE(_argPacks));
    1374             :     return _argPacks[argIndex][valueIndex].isAssigned();
    1375             :   }
    1376             : 
    1377             :   inline void assignReg(size_t argIndex, const BaseReg& reg, TypeId typeId = TypeId::kVoid) noexcept {
    1378             :     ASMJIT_ASSERT(argIndex < ASMJIT_ARRAY_SIZE(_argPacks));
    1379             :     ASMJIT_ASSERT(reg.isPhysReg());
    1380             :     _argPacks[argIndex][0].initReg(reg.type(), reg.id(), typeId);
    1381             :   }
    1382             : 
    1383             :   inline void assignReg(size_t argIndex, RegType regType, uint32_t regId, TypeId typeId = TypeId::kVoid) noexcept {
    1384             :     ASMJIT_ASSERT(argIndex < ASMJIT_ARRAY_SIZE(_argPacks));
    1385             :     _argPacks[argIndex][0].initReg(regType, regId, typeId);
    1386             :   }
    1387             : 
    1388             :   inline void assignStack(size_t argIndex, int32_t offset, TypeId typeId = TypeId::kVoid) noexcept {
    1389             :     ASMJIT_ASSERT(argIndex < ASMJIT_ARRAY_SIZE(_argPacks));
    1390             :     _argPacks[argIndex][0].initStack(offset, typeId);
    1391             :   }
    1392             : 
    1393             :   inline void assignRegInPack(size_t argIndex, size_t valueIndex, const BaseReg& reg, TypeId typeId = TypeId::kVoid) noexcept {
    1394             :     ASMJIT_ASSERT(argIndex < ASMJIT_ARRAY_SIZE(_argPacks));
    1395             :     ASMJIT_ASSERT(reg.isPhysReg());
    1396             :     _argPacks[argIndex][valueIndex].initReg(reg.type(), reg.id(), typeId);
    1397             :   }
    1398             : 
    1399             :   inline void assignRegInPack(size_t argIndex, size_t valueIndex, RegType regType, uint32_t regId, TypeId typeId = TypeId::kVoid) noexcept {
    1400             :     ASMJIT_ASSERT(argIndex < ASMJIT_ARRAY_SIZE(_argPacks));
    1401             :     _argPacks[argIndex][valueIndex].initReg(regType, regId, typeId);
    1402           0 :   }
    1403             : 
    1404             :   inline void assignStackInPack(size_t argIndex, size_t valueIndex, int32_t offset, TypeId typeId = TypeId::kVoid) noexcept {
    1405             :     ASMJIT_ASSERT(argIndex < ASMJIT_ARRAY_SIZE(_argPacks));
    1406             :     _argPacks[argIndex][valueIndex].initStack(offset, typeId);
    1407             :   }
    1408             : 
    1409             :   // NOTE: All `assignAll()` methods are shortcuts to assign all arguments at once, however, since registers are
    1410             :   // passed all at once these initializers don't provide any way to pass TypeId and/or to keep any argument between
    1411             :   // the arguments passed unassigned.
    1412             :   inline void _assignAllInternal(size_t argIndex, const BaseReg& reg) noexcept {
    1413             :     assignReg(argIndex, reg);
    1414             :   }
    1415             : 
    1416             :   template<typename... Args>
    1417             :   inline void _assignAllInternal(size_t argIndex, const BaseReg& reg, Args&&... args) noexcept {
    1418             :     assignReg(argIndex, reg);
    1419             :     _assignAllInternal(argIndex + 1, std::forward<Args>(args)...);
    1420             :   }
    1421             : 
    1422             :   template<typename... Args>
    1423             :   inline void assignAll(Args&&... args) noexcept {
    1424             :     _assignAllInternal(0, std::forward<Args>(args)...);
    1425             :   }
    1426             : 
    1427             :   //! \}
    1428             : 
    1429             :   //! \name Utilities
    1430             :   //! \{
    1431             : 
    1432             :   //! Update `FuncFrame` based on function's arguments assignment.
    1433             :   //!
    1434             :   //! \note You MUST call this in orher to use `BaseEmitter::emitArgsAssignment()`, otherwise the FuncFrame would
    1435             :   //! not contain the information necessary to assign all arguments into the registers and/or stack specified.
    1436             :   ASMJIT_API Error updateFuncFrame(FuncFrame& frame) const noexcept;
    1437             : 
    1438             :   //! \}
    1439             : };
    1440             : 
    1441             : //! \}
    1442             : 
    1443             : ASMJIT_END_NAMESPACE
    1444             : 
    1445             : #endif // ASMJIT_CORE_FUNC_H_INCLUDED
    1446             : 
    1447             : 
    1448             : #endif // __PLUMED_HAS_ASMJIT

Generated by: LCOV version 1.16