sm-ext-dhooks2/DynamicHooks/thirdparty/AsmJit/base/globals.h

667 lines
21 KiB
C++

// [AsmJit]
// Complete x86/x64 JIT and Remote Assembler for C++.
//
// [License]
// Zlib - See LICENSE.md file in the package.
// [Guard]
#ifndef _ASMJIT_BASE_GLOBALS_H
#define _ASMJIT_BASE_GLOBALS_H
// [Dependencies]
#include "../build.h"
// [Api-Begin]
#include "../apibegin.h"
namespace asmjit {
//! \addtogroup asmjit_base
//! \{
// ============================================================================
// [asmjit::TypeDefs]
// ============================================================================
//! AsmJit error core (unsigned integer).
typedef uint32_t Error;
//! 64-bit unsigned pointer, compatible with JIT and non-JIT generators.
//!
//! This is the preferred pointer type to use with AsmJit library. It has a
//! capability to hold any pointer for any architecture making it an ideal
//! candidate for a cross-platform code generator.
typedef uint64_t Ptr;
//! like \ref Ptr, but signed.
typedef int64_t SignedPtr;
// ============================================================================
// [asmjit::GlobalDefs]
// ============================================================================
//! Invalid index
//!
//! Invalid index is the last possible index that is never used in practice. In
//! AsmJit it is used exclusively with strings to indicate the the length of the
//! string is not known and has to be determined.
static const size_t kInvalidIndex = ~static_cast<size_t>(0);
//! Invalid base address.
static const Ptr kNoBaseAddress = static_cast<Ptr>(static_cast<SignedPtr>(-1));
//! Global constants.
ASMJIT_ENUM(GlobalDefs) {
//! Invalid value or operand id.
kInvalidValue = 0xFFFFFFFF,
//! Invalid register index.
kInvalidReg = 0xFF,
//! Invalid variable type.
kInvalidVar = 0xFF,
//! Host memory allocator overhead.
//!
//! The overhead is decremented from all zone allocators so the operating
//! system doesn't have to allocate one extra virtual page to keep tract of
//! the requested memory block.
//!
//! The number is actually a guess.
kMemAllocOverhead = sizeof(intptr_t) * 4,
//! Memory grow threshold.
//!
//! After the grow threshold is reached the capacity won't be doubled
//! anymore.
kMemAllocGrowMax = 8192 * 1024
};
// ============================================================================
// [asmjit::ArchId]
// ============================================================================
//! CPU architecture identifier.
ASMJIT_ENUM(ArchId) {
//! No/Unknown architecture.
kArchNone = 0,
//! X86 architecture (32-bit).
kArchX86 = 1,
//! X64 architecture (64-bit), also called AMD64.
kArchX64 = 2,
//! X32 architecture (64-bit with 32-bit pointers) (NOT USED ATM).
kArchX32 = 3,
//! Arm architecture (32-bit).
kArchArm32 = 4,
//! Arm64 architecture (64-bit).
kArchArm64 = 5,
#if ASMJIT_ARCH_X86
kArchHost = kArchX86
#elif ASMJIT_ARCH_X64
kArchHost = kArchX64
#elif ASMJIT_ARCH_ARM32
kArchHost = kArchArm32
#elif ASMJIT_ARCH_ARM64
kArchHost = kArchArm64
#else
# error "[asmjit] Unsupported host architecture."
#endif
};
// ============================================================================
// [asmjit::CallConv]
// ============================================================================
//! Function calling convention.
//!
//! Calling convention is a scheme that defines how function arguments are
//! passed and how the return value handled. In assembler programming it's
//! always needed to comply with function calling conventions, because even
//! small inconsistency can cause undefined behavior or application's crash.
//!
//! Platform Independent Conventions
//! --------------------------------
//!
//! - `kCallConvHost` - Should match the current C++ compiler native calling
//! convention.
//!
//! X86/X64 Specific Conventions
//! ----------------------------
//!
//! List of calling conventions for 32-bit x86 mode:
//! - `kCallConvX86CDecl` - Calling convention for C runtime.
//! - `kCallConvX86StdCall` - Calling convention for WinAPI functions.
//! - `kCallConvX86MsThisCall` - Calling convention for C++ members under
//! Windows (produced by MSVC and all MSVC compatible compilers).
//! - `kCallConvX86MsFastCall` - Fastest calling convention that can be used
//! by MSVC compiler.
//! - `kCallConvX86BorlandFastCall` - Borland fastcall convention.
//! - `kCallConvX86GccFastCall` - GCC fastcall convention (2 register arguments).
//! - `kCallConvX86GccRegParm1` - GCC regparm(1) convention.
//! - `kCallConvX86GccRegParm2` - GCC regparm(2) convention.
//! - `kCallConvX86GccRegParm3` - GCC regparm(3) convention.
//!
//! List of calling conventions for 64-bit x86 mode (x64):
//! - `kCallConvX64Win` - Windows 64-bit calling convention (WIN64 ABI).
//! - `kCallConvX64Unix` - Unix 64-bit calling convention (AMD64 ABI).
//!
//! ARM Specific Conventions
//! ------------------------
//!
//! List of ARM calling conventions:
//! - `kCallConvArm32SoftFP` - Legacy calling convention, floating point
//! arguments are passed via GP registers.
//! - `kCallConvArm32HardFP` - Modern calling convention, uses VFP registers
//! to pass floating point arguments.
ASMJIT_ENUM(CallConv) {
//! Calling convention is invalid (can't be used).
kCallConvNone = 0,
// --------------------------------------------------------------------------
// [X86]
// --------------------------------------------------------------------------
//! X86 `__cdecl` calling convention (used by C runtime and libraries).
//!
//! Compatible across MSVC and GCC.
//!
//! Arguments direction:
//! - Right to left.
//!
//! Stack is cleaned by:
//! - Caller.
//!
//! Return value:
//! - Integer types - `eax:edx` registers.
//! - Floating point - `fp0` register.
kCallConvX86CDecl = 1,
//! X86 `__stdcall` calling convention (used mostly by WinAPI).
//!
//! Compatible across MSVC and GCC.
//!
//! Arguments direction:
//! - Right to left.
//!
//! Stack is cleaned by:
//! - Callee.
//!
//! Return value:
//! - Integer types - `eax:edx` registers.
//! - Floating point - `fp0` register.
kCallConvX86StdCall = 2,
//! X86 `__thiscall` calling convention (MSVC/Intel specific).
//!
//! This is MSVC (and Intel) specific calling convention used when targeting
//! Windows platform for C++ class methods. Implicit `this` pointer (defined
//! as the first argument) is stored in `ecx` register instead of storing it
//! on the stack.
//!
//! This calling convention is implicitly used by MSVC for class functions.
//!
//! C++ class functions that have variable number of arguments use `__cdecl`
//! calling convention instead.
//!
//! Arguments direction:
//! - Right to left (except for the first argument passed in `ecx`).
//!
//! Stack is cleaned by:
//! - Callee.
//!
//! Return value:
//! - Integer types - `eax:edx` registers.
//! - Floating point - `fp0` register.
kCallConvX86MsThisCall = 3,
//! X86 `__fastcall` convention (MSVC/Intel specific).
//!
//! The first two arguments (evaluated from the left to the right) are passed
//! in `ecx` and `edx` registers, all others on the stack from the right to
//! the left.
//!
//! Arguments direction:
//! - Right to left (except for the first two integers passed in `ecx` and `edx`).
//!
//! Stack is cleaned by:
//! - Callee.
//!
//! Return value:
//! - Integer types - `eax:edx` registers.
//! - Floating point - `fp0` register.
//!
//! NOTE: This calling convention differs from GCC's one.
kCallConvX86MsFastCall = 4,
//! X86 `__fastcall` convention (Borland specific).
//!
//! The first two arguments (evaluated from the left to the right) are passed
//! in `ecx` and `edx` registers, all others on the stack from the left to
//! the right.
//!
//! Arguments direction:
//! - Left to right (except for the first two integers passed in `ecx` and `edx`).
//!
//! Stack is cleaned by:
//! - Callee.
//!
//! Return value:
//! - Integer types - `eax:edx` registers.
//! - Floating point - `fp0` register.
//!
//! NOTE: Arguments on the stack are in passed in left to right order, which
//! is really Borland specific, all other `__fastcall` calling conventions
//! use right to left order.
kCallConvX86BorlandFastCall = 5,
//! X86 `__fastcall` convention (GCC specific).
//!
//! The first two arguments (evaluated from the left to the right) are passed
//! in `ecx` and `edx` registers, all others on the stack from the right to
//! the left.
//!
//! Arguments direction:
//! - Right to left (except for the first two integers passed in `ecx` and `edx`).
//!
//! Stack is cleaned by:
//! - Callee.
//!
//! Return value:
//! - Integer types - `eax:edx` registers.
//! - Floating point - `fp0` register.
//!
//! NOTE: This calling convention should be compatible with `kCallConvX86MsFastCall`.
kCallConvX86GccFastCall = 6,
//! X86 `regparm(1)` convention (GCC specific).
//!
//! The first argument (evaluated from the left to the right) is passed in
//! `eax` register, all others on the stack from the right to the left.
//!
//! Arguments direction:
//! - Right to left (except for the first integer passed in `eax`).
//!
//! Stack is cleaned by:
//! - Caller.
//!
//! Return value:
//! - Integer types - `eax:edx` registers.
//! - Floating point - `fp0` register.
kCallConvX86GccRegParm1 = 7,
//! X86 `regparm(2)` convention (GCC specific).
//!
//! The first two arguments (evaluated from the left to the right) are passed
//! in `ecx` and `edx` registers, all others on the stack from the right to
//! the left.
//!
//! Arguments direction:
//! - Right to left (except for the first two integers passed in `ecx` and `edx`).
//!
//! Stack is cleaned by:
//! - Caller.
//!
//! Return value:
//! - Integer types - `eax:edx` registers.
//! - Floating point - `fp0` register.
kCallConvX86GccRegParm2 = 8,
//! X86 `regparm(3)` convention (GCC specific).
//!
//! Three first parameters (evaluated from left-to-right) are in
//! EAX:EDX:ECX registers, all others on the stack in right-to-left direction.
//!
//! Arguments direction:
//! - Right to left (except for the first three integers passed in `ecx`,
//! `edx`, and `ecx`).
//!
//! Stack is cleaned by:
//! - Caller.
//!
//! Return value:
//! - Integer types - `eax:edx` registers.
//! - Floating point - `fp0` register.
kCallConvX86GccRegParm3 = 9,
// --------------------------------------------------------------------------
// [X64]
// --------------------------------------------------------------------------
//! X64 calling convention used by Windows platform (WIN64-ABI).
//!
//! The first 4 arguments are passed in the following registers:
//! - 1. 32/64-bit integer in `rcx` and floating point argument in `xmm0`
//! - 2. 32/64-bit integer in `rdx` and floating point argument in `xmm1`
//! - 3. 32/64-bit integer in `r8` and floating point argument in `xmm2`
//! - 4. 32/64-bit integer in `r9` and floating point argument in `xmm3`
//!
//! If one or more argument from the first four doesn't match the list above
//! it is simply skipped. WIN64-ABI is very specific about this.
//!
//! All other arguments are pushed on the stack from the right to the left.
//! Stack has to be aligned by 16 bytes, always. There is also a 32-byte
//! shadow space on the stack that can be used to save up to four 64-bit
//! registers.
//!
//! Arguments direction:
//! - Right to left (except for all parameters passed in registers).
//!
//! Stack cleaned by:
//! - Caller.
//!
//! Return value:
//! - Integer types - `rax`.
//! - Floating point - `xmm0`.
//!
//! Stack is always aligned to 16 bytes.
//!
//! More information about this calling convention can be found on MSDN
//! <http://msdn.microsoft.com/en-us/library/9b372w95.aspx>.
kCallConvX64Win = 10,
//! X64 calling convention used by Unix platforms (AMD64-ABI).
//!
//! First six 32 or 64-bit integer arguments are passed in `rdi`, `rsi`,
//! `rdx`, `rcx`, `r8`, and `r9` registers. First eight floating point or xmm
//! arguments are passed in `xmm0`, `xmm1`, `xmm2`, `xmm3`, `xmm4`, `xmm5`,
//! `xmm6`, and `xmm7` registers.
//!
//! There is also a red zene below the stack pointer that can be used by the
//! function. The red zone is typically from [rsp-128] to [rsp-8], however,
//! red zone can also be disabled.
//!
//! Arguments direction:
//! - Right to left (except for all arguments passed in registers).
//!
//! Stack cleaned by:
//! - Caller.
//!
//! Return value:
//! - Integer types - `rax`.
//! - Floating point - `xmm0`.
//!
//! Stack is always aligned to 16 bytes.
kCallConvX64Unix = 11,
// --------------------------------------------------------------------------
// [ARM]
// --------------------------------------------------------------------------
kCallConvArm32SoftFP = 16,
kCallConvArm32HardFP = 17,
// --------------------------------------------------------------------------
// [Internal]
// --------------------------------------------------------------------------
//! \internal
_kCallConvX86Start = 1,
//! \internal
_kCallConvX86End = 9,
//! \internal
_kCallConvX64Start = 10,
//! \internal
_kCallConvX64End = 11,
//! \internal
_kCallConvArmStart = 16,
//! \internal
_kCallConvArmEnd = 17,
// --------------------------------------------------------------------------
// [Host]
// --------------------------------------------------------------------------
#if defined(ASMJIT_DOCGEN)
//! Default calling convention based on the current compiler's settings.
//!
//! NOTE: This should be always the same as `kCallConvHostCDecl`, but some
//! compilers allow to override the default calling convention. Overriding
//! is not detected at the moment.
kCallConvHost = DETECTED_AT_COMPILE_TIME,
//! Default C calling convention based on the current compiler's settings.
kCallConvHostCDecl = DETECTED_AT_COMPILE_TIME,
//! Compatibility for `__stdcall` calling convention.
//!
//! NOTE: This enumeration is always set to a value which is compatible with
//! the current compiler's `__stdcall` calling convention. In 64-bit mode
//! there is no such convention and the value is mapped to `kCallConvX64Win`
//! or `kCallConvX64Unix`, depending on the host architecture.
kCallConvHostStdCall = DETECTED_AT_COMPILE_TIME,
//! Compatibility for `__fastcall` calling convention.
//!
//! NOTE: This enumeration is always set to a value which is compatible with
//! the current compiler's `__fastcall` calling convention. In 64-bit mode
//! there is no such convention and the value is mapped to `kCallConvX64Win`
//! or `kCallConvX64Unix`, depending on the host architecture.
kCallConvHostFastCall = DETECTED_AT_COMPILE_TIME
#elif ASMJIT_ARCH_X86
// X86 Host Support.
kCallConvHost = kCallConvX86CDecl,
kCallConvHostCDecl = kCallConvX86CDecl,
kCallConvHostStdCall = kCallConvX86StdCall,
kCallConvHostFastCall =
ASMJIT_CC_MSC ? kCallConvX86MsFastCall :
ASMJIT_CC_GCC ? kCallConvX86GccFastCall :
ASMJIT_CC_CLANG ? kCallConvX86GccFastCall :
ASMJIT_CC_CODEGEAR ? kCallConvX86BorlandFastCall : kCallConvNone
#elif ASMJIT_ARCH_X64
// X64 Host Support.
kCallConvHost = ASMJIT_OS_WINDOWS ? kCallConvX64Win : kCallConvX64Unix,
// These don't exist in 64-bit mode.
kCallConvHostCDecl = kCallConvHost,
kCallConvHostStdCall = kCallConvHost,
kCallConvHostFastCall = kCallConvHost
#elif ASMJIT_ARCH_ARM32
# if defined(__SOFTFP__)
kCallConvHost = kCallConvArm32SoftFP,
# else
kCallConvHost = kCallConvArm32HardFP,
# endif
// These don't exist on ARM.
kCallConvHostCDecl = kCallConvHost,
kCallConvHostStdCall = kCallConvHost,
kCallConvHostFastCall = kCallConvHost
#else
# error "[asmjit] Couldn't determine the target's calling convention."
#endif
};
// ============================================================================
// [asmjit::ErrorCode]
// ============================================================================
//! AsmJit error codes.
ASMJIT_ENUM(ErrorCode) {
//! No error (success).
//!
//! This is default state and state you want.
kErrorOk = 0,
//! Heap memory allocation failed.
kErrorNoHeapMemory,
//! Virtual memory allocation failed.
kErrorNoVirtualMemory,
//! Invalid argument.
kErrorInvalidArgument,
//! Invalid state.
kErrorInvalidState,
//! Invalid architecture.
kErrorInvalidArch,
//! The object is not initialized.
kErrorNotInitialized,
//! No code generated.
//!
//! Returned by runtime if the code-generator contains no code.
kErrorNoCodeGenerated,
//! Code generated is too large to fit in memory reserved.
//!
//! Returned by `StaticRuntime` in case that the code generated is too large
//! to fit in the memory already reserved for it.
kErrorCodeTooLarge,
//! Label is already bound.
kErrorLabelAlreadyBound,
//! Unknown instruction (an instruction ID is out of bounds or instruction
//! name is invalid).
kErrorUnknownInst,
//! Illegal instruction.
//!
//! This status code can also be returned in X64 mode if AH, BH, CH or DH
//! registers have been used together with a REX prefix. The instruction
//! is not encodable in such case.
//!
//! Example of raising `kErrorIllegalInst` error.
//!
//! ~~~
//! // Invalid address size.
//! a.mov(dword_ptr(eax), al);
//!
//! // Undecodable instruction - AH used with R10, however R10 can only be
//! // encoded by using REX prefix, which conflicts with AH.
//! a.mov(byte_ptr(r10), ah);
//! ~~~
//!
//! NOTE: In debug mode assertion is raised instead of returning an error.
kErrorIllegalInst,
//! Illegal (unencodable) addressing used.
kErrorIllegalAddresing,
//! Illegal (unencodable) displacement used.
//!
//! X86/X64 Specific
//! ----------------
//!
//! Short form of jump instruction has been used, but the displacement is out
//! of bounds.
kErrorIllegalDisplacement,
//! A variable has been assigned more than once to a function argument (Compiler).
kErrorOverlappedArgs,
//! Count of AsmJit error codes.
kErrorCount
};
//! \}
// ============================================================================
// [asmjit::Init / NoInit]
// ============================================================================
#if !defined(ASMJIT_DOCGEN)
struct _Init {};
static const _Init Init = {};
struct _NoInit {};
static const _NoInit NoInit = {};
#endif // !ASMJIT_DOCGEN
// ============================================================================
// [asmjit::DebugUtils]
// ============================================================================
namespace DebugUtils {
//! Get a printable version of `asmjit::Error` value.
ASMJIT_API const char* errorAsString(Error err) noexcept;
//! \addtogroup asmjit_base
//! \{
//! Called in debug build to output a debugging message caused by assertion
//! failure or tracing.
ASMJIT_API void debugOutput(const char* str) noexcept;
//! Called in debug build on assertion failure.
//!
//! \param file Source file name where it happened.
//! \param line Line in the source file.
//! \param msg Message to display.
//!
//! If you have problems with assertions put a breakpoint at assertionFailed()
//! function (asmjit/base/globals.cpp) and check the call stack to locate the
//! failing code.
ASMJIT_API void ASMJIT_NORETURN assertionFailed(const char* file, int line, const char* msg) noexcept;
//! \}
} // DebugUtils namespace
} // asmjit namespace
// ============================================================================
// [ASMJIT_ASSERT]
// ============================================================================
#if defined(ASMJIT_DEBUG)
# define ASMJIT_ASSERT(exp) \
do { \
if (!(exp)) { \
::asmjit::DebugUtils::assertionFailed( \
__FILE__ + ::asmjit::DebugUtils::kSourceRelativePathOffset, \
__LINE__, \
#exp); \
} \
} while (0)
# define ASMJIT_NOT_REACHED() \
::asmjit::DebugUtils::assertionFailed( \
__FILE__ + ::asmjit::DebugUtils::kSourceRelativePathOffset, \
__LINE__, \
"MUST NOT BE REACHED")
#else
# define ASMJIT_ASSERT(exp) ASMJIT_NOP
# define ASMJIT_NOT_REACHED() ASMJIT_ASSUME(0)
#endif // DEBUG
// ============================================================================
// [ASMJIT_PROPAGATE_ERROR]
// ============================================================================
//! \internal
//!
//! Used by AsmJit to return the `_Exp_` result if it's an error.
#define ASMJIT_PROPAGATE_ERROR(_Exp_) \
do { \
::asmjit::Error _errval = (_Exp_); \
if (_errval != ::asmjit::kErrorOk) \
return _errval; \
} while (0)
// ============================================================================
// [asmjit_cast<>]
// ============================================================================
//! \addtogroup asmjit_base
//! \{
//! Cast used to cast pointer to function. It's like reinterpret_cast<>,
//! but uses internally C style cast to work with MinGW.
//!
//! If you are using single compiler and `reinterpret_cast<>` works for you,
//! there is no reason to use `asmjit_cast<>`. If you are writing
//! cross-platform software with various compiler support, consider using
//! `asmjit_cast<>` instead of `reinterpret_cast<>`.
template<typename T, typename Z>
static ASMJIT_INLINE T asmjit_cast(Z* p) noexcept { return (T)p; }
//! \}
// [Api-End]
#include "../apiend.h"
// [Guard]
#endif // _ASMJIT_BASE_GLOBALS_H