RCSStreamingMelter/deps/asmjit/include/CompilerX86X64.h
// AsmJit - Complete JIT Assembler for C++ Language.
// Copyright (c) 2008-2009, Petr Kobalicek <kobalicek.petr@gmail.com>
//
// Permission is hereby granted, free of charge, to any person
// obtaining a copy of this software and associated documentation
// files (the "Software"), to deal in the Software without
// restriction, including without limitation the rights to use,
// copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the
// Software is furnished to do so, subject to the following
// conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
// OTHER DEALINGS IN THE SOFTWARE.
// [Guard]
#ifndef _ASMJIT_COMPILERX86X64_H
#define _ASMJIT_COMPILERX86X64_H
#if !defined(_ASMJIT_COMPILER_H)
#warning "AsmJit/CompilerX86X64 can be only included by AsmJit/Compiler.h"
#endif // _ASMJIT_COMPILER_H
// [Dependencies]
#include "Build.h"
#include "Assembler.h"
#include "Serializer.h"
#include "Util.h"
#include <string.h>
// A little bit C++.
#include <new>
// [Warnings-Push]
#include "WarningsPush.h"
namespace AsmJit {
//! @addtogroup AsmJit_Compiler
//! @{
// ============================================================================
// [Constants]
// ============================================================================
//! @brief Calling convention type.
//!
//! Calling convention is scheme how function arguments are passed into
//! function and how functions returns values. In assembler programming
//! it's needed to always comply with function calling conventions, because
//! even small inconsistency can cause undefined behavior or crash.
//!
//! List of calling conventions for 32 bit x86 mode:
//! - @c CALL_CONV_CDECL - Calling convention for C runtime.
//! - @c CALL_CONV_STDCALL - Calling convention for WinAPI functions.
//! - @c CALL_CONV_MSTHISCALL - Calling convention for C++ members under
//! Windows (produced by MSVC and all MSVC compatible compilers).
//! - @c CALL_CONV_MSFASTCALL - Fastest calling convention that can be used
//! by MSVC compiler.
//! - @c CALL_CONV_BORNANDFASTCALL - Borland fastcall convention.
//! - @c CALL_CONV_GCCFASTCALL_2 - GCC fastcall convention with 2 register
//! arguments.
//! - @c CALL_CONV_GCCFASTCALL_3 - GCC fastcall convention with 3 register
//! arguments.
//!
//! List of calling conventions for 64 bit x86 mode (x64):
//! - @c CALL_CONV_X64W - Windows 64 bit calling convention (WIN64 ABI).
//! - @c CALL_CONV_X64U - Unix 64 bit calling convention (AMD64 ABI).
//!
//! There is also @c CALL_CONV_DEFAULT that is defined to fit best to your
//! compiler.
//!
//! These types are used together with @c AsmJit::Compiler::newFunction()
//! method.
enum CALL_CONV
{
//! @brief Calling convention is invalid (can't be used).
CALL_CONV_NONE = 0,
// [X64 Calling Conventions]
//! @brief X64 calling convention for Windows platform.
//!
//! For first four arguments are used these registers:
//! - 1. 32/64 bit integer or floating point argument - rcx/xmm0
//! - 2. 32/64 bit integer or floating point argument - rdx/xmm1
//! - 3. 32/64 bit integer or floating point argument - r8/xmm2
//! - 4. 32/64 bit integer or floating point argument - r9/xmm3
//!
//! Note first four arguments here means arguments at positions from 1 to 4
//! (included). For example if second argument is not passed by register then
//! rdx/xmm1 register is unused.
//!
//! All other arguments are pushed on the stack in right-to-left direction.
//! Stack is aligned by 16 bytes. There is 32 bytes shadow space on the stack
//! that can be used to save up to four 64 bit registers (probably designed to
//! be used to save first four arguments passed in registers).
//!
//! Arguments direction:
//! - Right to Left (except for first 4 parameters that's in registers)
//!
//! Stack is cleaned by:
//! - Caller.
//!
//! Return value:
//! - Integer types - RAX register.
//! - Floating points - XMM0 register.
//!
//! Stack is always aligned by 16 bytes.
//!
//! More informations about this calling convention can be found on MSDN:
//! http://msdn.microsoft.com/en-us/library/9b372w95.aspx .
CALL_CONV_X64W = 1,
//! @brief X64 calling convention for Unix platforms (AMD64 ABI).
//!
//! First six 32 or 64 bit integer arguments are passed in rdi, rsi, rdx,
//! rcx, r8, r9 registers. First eight floating point or XMM arguments
//! are passed in xmm0, xmm1, xmm2, xmm3, xmm4, xmm5, xmm6, xmm7 registers.
//! This means that in registers can be transferred up to 14 arguments total.
//!
//! There is also RED ZONE below the stack pointer that can be used for
//! temporary storage. The red zone is the space from [rsp-128] to [rsp-8].
//!
//! Arguments direction:
//! - Right to Left (Except for arguments passed in registers).
//!
//! Stack is cleaned by:
//! - Caller.
//!
//! Return value:
//! - Integer types - RAX register.
//! - Floating points - XMM0 register.
//!
//! Stack is always aligned by 16 bytes.
CALL_CONV_X64U = 2,
// [X86 Calling Conventions]
//! @brief Cdecl calling convention (used by C runtime).
//!
//! Compatible across MSVC and GCC.
//!
//! Arguments direction:
//! - Right to Left
//!
//! Stack is cleaned by:
//! - Caller.
CALL_CONV_CDECL = 3,
//! @brief Stdcall calling convention (used 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 points - st(0) register.
CALL_CONV_STDCALL = 4,
//! @brief MSVC specific calling convention used by MSVC/Intel compilers
//! for struct/class methods.
//!
//! This is MSVC (and Intel) only calling convention used in Windows
//! world for C++ class methods. Implicit 'this' pointer is stored in
//! ECX register instead of storing it on the stack.
//!
//! Arguments direction:
//! - Right to Left (except this pointer in ECX)
//!
//! Stack is cleaned by:
//! - Callee.
//!
//! Return value:
//! - Integer types - EAX:EDX registers.
//! - Floating points - st(0) register.
//!
//! C++ class methods that have variable count of arguments uses different
//! calling convention called cdecl.
//!
//! @note This calling convention is always used by MSVC for class methods,
//! it's implicit and there is no way how to override it.
CALL_CONV_MSTHISCALL = 5,
//! @brief MSVC specific fastcall.
//!
//! Two first parameters (evaluated from left-to-right) are in ECX:EDX
//! registers, all others on the stack in right-to-left order.
//!
//! Arguments direction:
//! - Right to Left (except to first two integer arguments in ECX:EDX)
//!
//! Stack is cleaned by:
//! - Callee.
//!
//! Return value:
//! - Integer types - EAX:EDX registers.
//! - Floating points - st(0) register.
//!
//! @note This calling convention differs to GCC one in stack cleaning
//! mechanism.
CALL_CONV_MSFASTCALL = 6,
//! @brief Borland specific fastcall with 2 parameters in registers.
//!
//! Two first parameters (evaluated from left-to-right) are in ECX:EDX
//! registers, all others on the stack in left-to-right order.
//!
//! Arguments direction:
//! - Left to Right (except to first two integer arguments in ECX:EDX)
//!
//! Stack is cleaned by:
//! - Callee.
//!
//! Return value:
//! - Integer types - EAX:EDX registers.
//! - Floating points - st(0) register.
//!
//! @note Arguments on the stack are in left-to-right order that differs
//! to other fastcall conventions used in different compilers.
CALL_CONV_BORLANDFASTCALL = 7,
//! @brief GCC specific fastcall with 2 parameters in registers.
//!
//! Two first parameters (evaluated from left-to-right) are in ECX:EDX
//! registers, all others on the stack in right-to-left order.
//!
//! Arguments direction:
//! - Right to Left (except to first two integer arguments in ECX:EDX)
//!
//! Stack is cleaned by:
//! - Caller.
//!
//! Return value:
//! - Integer types - EAX:EDX registers.
//! - Floating points - st(0) register.
//!
//! @note This calling convention differs to MSVC one in stack cleaning
//! mechanism.
CALL_CONV_GCCFASTCALL_2 = 8,
//! @brief GCC specific fastcall with 3 parameters in registers.
//!
//! Three first parameters (evaluated from left-to-right) are in
//! ECX:EDX:EAX registers, all others on the stack in right-to-left order.
//!
//! Arguments direction:
//! - Right to Left (except to first three integer arguments in ECX:EDX:EAX)
//!
//! Stack is cleaned by:
//! - Caller.
//!
//! Return value:
//! - Integer types - EAX:EDX registers.
//! - Floating points - st(0) register.
CALL_CONV_GCCFASTCALL_3 = 9,
// [Preferred Calling Convention]
//! @def CALL_CONV_DEFAULT
//! @brief Default calling convention for current platform / operating
//! system.
#if defined(ASMJIT_X86)
CALL_CONV_DEFAULT = CALL_CONV_CDECL
#else
# if defined(WIN32) || defined(_WIN32) || defined(WINDOWS)
CALL_CONV_DEFAULT = CALL_CONV_X64W
# else
CALL_CONV_DEFAULT = CALL_CONV_X64U
# endif
#endif // ASMJIT_X86
};
//! @brief Variable type.
//!
//! Variable type is used by @c AsmJit::Function::newVariable() method and can
//! be also retrieved by @c AsmJit::VariableRef::type().
enum VARIABLE_TYPE
{
//! @brief Invalid variable type (don't use).
VARIABLE_TYPE_NONE = 0,
//! @brief Variable is 32 bit integer (@c Int32).
VARIABLE_TYPE_INT32 = 1,
//! @brief Variable is 32 bit unsigned integer (@c UInt32).
VARIABLE_TYPE_UINT32 = 1,
//! @var VARIABLE_TYPE_INT64
//! @brief Variable is 64 bit signed integer (@c Int64).
//! @note Can be used only in 64 bit mode.
//! @var VARIABLE_TYPE_UINT64
//! @brief Variable is 64 bit unsigned integer (@c UInt64).
//! @note Can be used only in 64 bit mode.
//! @var VARIABLE_TYPE_SYSINT
//! @brief Variable is system wide integer (@c Int32 or @c Int64).
//! @var VARIABLE_TYPE_SYSUINT
//! @brief Variable is system wide unsigned integer (@c UInt32 or @c UInt64).
#if defined(ASMJIT_X86)
VARIABLE_TYPE_SYSINT = VARIABLE_TYPE_INT32,
VARIABLE_TYPE_SYSUINT = VARIABLE_TYPE_UINT32,
#else
VARIABLE_TYPE_INT64 = 2,
VARIABLE_TYPE_UINT64 = 2,
VARIABLE_TYPE_SYSINT = VARIABLE_TYPE_INT64,
VARIABLE_TYPE_SYSUINT = VARIABLE_TYPE_UINT64,
#endif
//! @brief Variable is pointer or reference to memory (or any type).
VARIABLE_TYPE_PTR = VARIABLE_TYPE_SYSUINT,
//! @brief Variable is X87 (FPU) SP-FP number (float).
//!
//! TODO: Float registers allocation is not supported.
VARIABLE_TYPE_X87_FLOAT = 3,
//! @brief Variable is X87 (FPU) DP-FP number (double).
//!
//! TODO: Double registers allocation is not supported.
VARIABLE_TYPE_X87_DOUBLE = 4,
//! @brief Variable is SSE scalar SP-FP number.
VARIABLE_TYPE_XMM_FLOAT = 5,
//! @brief Variable is SSE2 scalar DP-FP number.
VARIABLE_TYPE_XMM_DOUBLE = 6,
//! @brief Variable is SSE packed SP-FP number (4 floats).
VARIABLE_TYPE_XMM_FLOAT_4 = 7,
//! @brief Variable is SSE2 packed DP-FP number (2 doubles).
VARIABLE_TYPE_XMM_DOUBLE_2 = 8,
#if defined(ASMJIT_X86)
VARIABLE_TYPE_FLOAT = VARIABLE_TYPE_X87_FLOAT,
VARIABLE_TYPE_DOUBLE = VARIABLE_TYPE_X87_DOUBLE,
#else
VARIABLE_TYPE_FLOAT = VARIABLE_TYPE_XMM_FLOAT,
VARIABLE_TYPE_DOUBLE = VARIABLE_TYPE_XMM_DOUBLE,
#endif
//! @brief Variable is MM register / memory location.
VARIABLE_TYPE_MM = 9,
//! @brief Variable is XMM register / memory location.
VARIABLE_TYPE_XMM = 10,
//! @brief Count of variable types.
_VARIABLE_TYPE_COUNT
};
// ============================================================================
// [AsmJit::Variable]
// ============================================================================
//! @brief Variable.
//!
//! Variables reresents registers or memory locations that can be allocated by
//! @c Function. Each function arguments are also allocated as variables, so
//! accessing function arguments is similar to accessing function variables.
//!
//! Variables can be declared by @c AsmJit::Function::newVariable() or by
//! declaring function arguments by AsmJit::Compiler::newFunction(). Compiler
//! always returns variables as pointers to @c Variable instance.
//!
//! Variable instances are never accessed directly, instead there are wrappers.
//! Wrappers are designed to simplify variables management and it's lifetime.
//! Because variables are based on reference counting, each variable that is
//! returned from @c Compiler needs to be wrapped in @c VariableRef or similar
//! (@c Int32Ref, @c Int64Ref, @c SysIntRef, @c PtrRef, @c MMRef, @c XMMRef)
//! classes. Each wrapper class is designed to wrap specific variable type. For
//! example integer should be always wrapped into @c Int32Ref or @c Int64Ref,
//! MMX register to @c MMRef, etc...
//!
//! Variable wrapping is also needed, because it's lifetime is based on
//! reference counting. Each variable returned from compiler has reference
//! count equal to zero! So wrapper class increases it to one (or more that
//! depends how much you wrapped it) and destroying wrapper means decreasing
//! reference count. If reference count is decreased to zero, variable life
//! ends, it's marked as unused and compiler can reuse it later.
//!
//! @sa @c VariableRef, @c Int32Ref, @c Int64Ref, @c SysIntRef, @c PtrRef,
//! @c MMRef, @c XMMRef.
struct ASMJIT_API Variable
{
// [Typedefs]
//! @brief Custom alloc function type.
typedef void (*AllocFn)(Variable* v);
//! @brief Custom spill function type.
typedef void (*SpillFn)(Variable* v);
// [Construction / Destruction]
//! @brief Create a new @a Variable instance.
//!
//! Always use @c AsmJit::Function::newVariable() method to create
//! @c Variable.
Variable(Compiler* c, Function* f, UInt8 type);
//! @brief Destroy variable instance.
//!
//! Never destroy @c Variable instance created by @c Compiler.
virtual ~Variable();
// [Methods]
//! @brief Return compiler that owns this variable.
inline Compiler* compiler() const ASMJIT_NOTHROW
{ return _compiler; }
//! @brief Return function that owns this variable.
inline Function* function() const ASMJIT_NOTHROW
{ return _function; }
//! @brief Return reference count.
inline SysUInt refCount() const ASMJIT_NOTHROW
{ return _refCount; }
//! @brief Return spill count.
inline SysUInt spillCount() const ASMJIT_NOTHROW
{ return _spillCount; }
//! @brief Return life id.
inline SysUInt lifeId() const ASMJIT_NOTHROW
{ return _lifeId; }
//! @brief Return register access statistics.
inline SysUInt registerAccessCount() const ASMJIT_NOTHROW
{ return _registerAccessCount; }
//! @brief Return memory access statistics.
inline SysUInt memoryAccessCount() const ASMJIT_NOTHROW
{ return _memoryAccessCount; }
//! @brief Return variable type, see @c VARIABLE_TYPE.
inline UInt8 type() const ASMJIT_NOTHROW
{ return _type; }
//! @brief Return variable size (in bytes).
inline UInt8 size() const ASMJIT_NOTHROW
{ return _size; }
//! @brief Return variable state, see @c VARIABLE_STATE.
inline UInt8 state() const ASMJIT_NOTHROW
{ return _state; }
//! @brief Return variable priority.
//!
//! Variable priority is used for spilling. Lower number means less chance
//! to spill. Zero means that variable can't be never spilled.
inline UInt8 priority() const ASMJIT_NOTHROW
{ return _priority; }
//! @brief Return variable register code (where it now lives), or NO_REG if
//! it's only in memory (spilled).
inline UInt8 registerCode() const ASMJIT_NOTHROW
{ return _registerCode; }
//! @brief Return variable preferred register.
inline UInt8 preferredRegisterCode() const ASMJIT_NOTHROW
{ return _preferredRegisterCode; }
//! @brief Return variable home register code (this is usually last
//! allocated register code).
inline UInt8 homeRegisterCode() const ASMJIT_NOTHROW
{ return _homeRegisterCode; }
//! @brief Return variable changed state.
inline UInt8 changed() const ASMJIT_NOTHROW
{ return _changed; }
//! @brief Return whether variable is reusable.
inline UInt8 reusable() const ASMJIT_NOTHROW
{ return _reusable; }
//! @brief Return whether variable contains custom memory home address.
inline UInt8 customMemoryHome() const ASMJIT_NOTHROW
{ return _customMemoryHome; }
//! @brief Return whether variable contains custom memory home address.
inline UInt8 stackArgument() const ASMJIT_NOTHROW
{ return _stackArgument; }
//! @brief Return variable stack offset.
//!
//! @note Stack offsets can be changed by Compiler, don't use this
//! to generate memory operands.
inline SysInt stackOffset() const ASMJIT_NOTHROW
{ return _stackOffset; }
//! @brief Set variable priority.
void setPriority(UInt8 priority);
//! @brief Set varialbe preferred register.
inline void setPreferredRegisterCode(UInt8 code) ASMJIT_NOTHROW
{ _preferredRegisterCode = code; }
//! @brief Set variable changed state.
inline void setChanged(UInt8 changed) ASMJIT_NOTHROW
{ _changed = changed; }
//! @brief Memory operand that will be always pointed to variable memory address. */
inline const Mem& memoryOperand() const ASMJIT_NOTHROW
{ return *_memoryOperand; }
void setMemoryHome(const Mem& memoryHome) ASMJIT_NOTHROW;
// [Reference counting]
//! @brief Increase reference count and return itself.
Variable* ref();
//! @brief Decrease reference count. If reference is decreased to zero,
//! variable is marked as unused and deallocated.
void deref();
// [Code Generation]
//! @brief Allocate variable to register.
//! @param mode Allocation mode (see @c VARIABLE_ALLOC enum)
//! @param preferredRegister Preferred register to use (see @c AsmJit::REG enum).
inline bool alloc(
UInt8 mode = VARIABLE_ALLOC_READWRITE,
UInt8 preferredRegister = NO_REG);
//! @brief Allocate variable to register and return it in @a dest.
//!
//! This function is similar to @c alloc(), but setups @a dest register. It's
//! convenience method for @c AsmJit::VariableRef register accesses.
void getReg(
UInt8 mode, UInt8 preferredRegister,
BaseReg* dest, UInt8 regType);
const Mem& m();
//! @brief Spill variable (move to memory).
inline bool spill();
//! @brief Unuse variable
//!
//! This will completely destroy variable. After @c unuse() you can use
//! @c alloc() to allocate it again.
inline void unuse();
// [Custom Spill / Restore]
//! @brief Return @c true if this variable uses custom alloc or spill
//! functions (this means bypassing built-in functions).
//!
//! @note If alloc or spill function is set, variable is marked as custom
//! and there is no chance to move it to / from stack. For example mmx zero
//! register can be implemented in allocFn() that will emit pxor(mm, mm).
//! Variable will never spill into stack in this case.
inline bool isCustom() const ASMJIT_NOTHROW
{ return _allocFn != NULL || _spillFn != NULL; }
//! @brief Get custom alloc function.
inline AllocFn allocFn() const ASMJIT_NOTHROW
{ return _allocFn; }
//! @brief Get custom spill function.
inline SpillFn spillFn() const ASMJIT_NOTHROW
{ return _spillFn; }
//! @brief Get custom data pointer.
inline void* dataPtr() const ASMJIT_NOTHROW
{ return _dataPtr; }
//! @brief Get custom data pointer.
inline SysInt dataInt() const ASMJIT_NOTHROW
{ return _dataInt; }
//! @brief Set custom alloc function.
inline void setAllocFn(AllocFn fn) ASMJIT_NOTHROW
{ _allocFn = fn; }
//! @brief Set custom spill function.
inline void setSpillFn(SpillFn fn) ASMJIT_NOTHROW
{ _spillFn = fn; }
//! @brief Set custom data pointer.
inline void setDataPtr(void* data) ASMJIT_NOTHROW
{ _dataPtr = data; }
//! @brief Set custom data integer.
inline void setDataInt(SysInt data) ASMJIT_NOTHROW
{ _dataInt = data; }
//! @brief Get variable name.
inline const char* name() const ASMJIT_NOTHROW
{ return _name; }
//! @brief Set variable name.
void setName(const char* name) ASMJIT_NOTHROW;
private:
//! @brief Set variable stack offset.
//! @internal
inline void setStackOffset(SysInt stackOffset) ASMJIT_NOTHROW
{ _stackOffset = stackOffset; }
//! @brief Set most members by one shot.
inline void setAll(
UInt8 type, UInt8 size, UInt8 state, UInt8 priority,
UInt8 registerCode, UInt8 preferredRegisterCode,
SysInt stackOffset) ASMJIT_NOTHROW
{
_type = type;
_size = size;
_state = state;
_priority = priority;
_registerCode = registerCode;
_preferredRegisterCode = preferredRegisterCode;
_stackOffset = stackOffset;
}
//! @brief Compiler that owns this variable.
Compiler* _compiler;
//! @brief Function that owns this variable.
Function* _function;
//! @brief Reference count.
SysUInt _refCount;
//! @brief How many times was variable spilled (in current context).
SysUInt _spillCount;
//! @brief Register access count (in current context).
SysUInt _registerAccessCount;
//! @brief Memory access count (in current context).
SysUInt _memoryAccessCount;
//! @brief Current variable life ID (also means how many times was variable reused).
SysUInt _lifeId;
//! @brief How many times was variable spilled (in current context).
SysUInt _globalSpillCount;
//! @brief Register access count (in current context).
SysUInt _globalRegisterAccessCount;
//! @brief Memory access count (in current context).
SysUInt _globalMemoryAccessCount;
//! @brief Variable type, see @c VARIABLE_TYPE.
UInt8 _type;
//! @brief Variable size (in bytes).
UInt8 _size;
//! @brief Variable state, see @c VARIABLE_STATE.
UInt8 _state;
//! @brief Variable priority.
UInt8 _priority;
//! @brief Register code if variable state is @c VARIABLE_STATE_REGISTER.
UInt8 _registerCode;
//! @brief Default register where to alloc variable.
UInt8 _preferredRegisterCode;
//! @brief Last allocated register code.
UInt8 _homeRegisterCode;
//! @brief true if variable in register was changed and when spilling it
//! needs to be copied into memory location.
UInt8 _changed;
//! @brief Whether variable can be reused.
UInt8 _reusable;
//! @brief Whether variable contains custom home address.
UInt8 _customMemoryHome;
//! @brief Whether variable is function argument passed onto the stack.
UInt8 _stackArgument;
//! @brief Stack location.
SysInt _stackOffset;
//! @brief Variable memory operand.
Mem* _memoryOperand;
//! @brief Custom alloc function (or NULL).
AllocFn _allocFn;
//! @brief Custom spill function (or NULL).
SpillFn _spillFn;
//! @brief Custom void* data that can be used by custom spill and restore functions.
void* _dataPtr;
//! @brief Custom integer that can be used by custom spill and restore functions.
SysInt _dataInt;
//! @brief Variable name.
char _name[MAX_VARIABLE_LENGTH];
friend struct CompilerCore;
friend struct Function;
friend struct VariableRef;
// Disable copy.
ASMJIT_DISABLE_COPY(Variable);
};
// ============================================================================
// [AsmJit::XXXRef]
// ============================================================================
//! @brief Base class for variable wrappers.
//!
//! @c VariableRef class is designed to manage @c Variable instances. It's
//! based on reference counting and if reference gets to zero (in destructor),
//! variable is freed by compiler. This helps with scoping variables and
//! minimizes mistakes that can be done with manual allocation / freeing.
//!
//! @note Compiler can reuse existing variables if reference gets zero.
//!
//! @sa @c Variable,
//! @c Int32Ref, @c Int64Ref, @c SysIntRef, @c PtrRef, @c MMRef, @c XMMRef.
struct ASMJIT_HIDDEN VariableRef
{
// [Typedefs]
typedef Variable::AllocFn AllocFn;
typedef Variable::SpillFn SpillFn;
// [Construction / Destruction]
//! @brief Create new uninitialized variable reference.
//!
//! Using uninitialized variable reference is forbidden.
inline VariableRef() : _v(NULL) {}
//! @brief Reference variable @a v (@a v can't be @c NULL).
inline VariableRef(Variable* v) : _v(v->ref()) {}
//! @brief Dereference variable if it's wrapped.
inline ~VariableRef() { if (_v) _v->deref(); }
inline VariableRef& operator=(Variable* v)
{
use(v); return *this;
}
//! @brief Return @c Variable instance.
inline Variable* v() const { return _v; }
// [Methods]
//! @brief Return variable type, see @c VARIABLE_TYPE.
inline UInt8 type() const { ASMJIT_ASSERT(_v); return _v->type(); }
//! @brief Return variable size (in bytes).
inline UInt8 size() const { ASMJIT_ASSERT(_v); return _v->size(); }
//! @brief Return variable state, see @c VARIABLE_STATE.
inline UInt8 state() const { ASMJIT_ASSERT(_v); return _v->state(); }
void use(Variable* v)
{
Variable* tmp = v->ref();
if (_v) _v->deref();
_v = tmp;
}
//! @brief Allocate variable to register.
//! @param mode Allocation mode (see @c VARIABLE_ALLOC enum)
//! @param preferredRegister Preferred register to use (see @c REG enum).
inline bool alloc(
UInt8 mode = VARIABLE_ALLOC_READWRITE,
UInt8 preferredRegister = NO_REG)
{
ASMJIT_ASSERT(_v);
return _v->alloc(mode, preferredRegister);
}
//! @brief Spill variable (move to memory).
inline bool spill()
{
ASMJIT_ASSERT(_v);
return _v->spill();
}
//! @brief Unuse variable
//!
//! This will completely destroy variable. After @c unuse() you can use
//! @c alloc() to allocate it again.
inline void unuse()
{
if (_v) _v->unuse();
}
//! @brief Destroy variable (@c VariableRef can't be used anymore after destroy).
inline void destroy()
{
if (_v)
{
_v->deref();
_v = NULL;
}
}
//! @brief Get variable preferred register code.
inline UInt8 preferredRegisterCode() const
{ ASMJIT_ASSERT(_v); return _v->preferredRegisterCode(); }
//! @brief Set variable preferred register code to @a code.
//! @param code Preferred register code (see @c AsmJit::REG enum).
inline void setPreferredRegisterCode(UInt8 code)
{ ASMJIT_ASSERT(_v); _v->setPreferredRegisterCode(code); }
//! @brief Get variable home register code.
inline UInt8 homeRegisterCode() const
{ ASMJIT_ASSERT(_v); return _v->homeRegisterCode(); }
//! @brief Get variable priority.
inline UInt8 priority() const
{ ASMJIT_ASSERT(_v); return _v->priority(); }
//! @brief Set variable priority.
inline void setPriority(UInt8 priority)
{ ASMJIT_ASSERT(_v); _v->setPriority(priority); }
//! @brief Return if variable changed state.
inline UInt8 changed() const
{ ASMJIT_ASSERT(_v); return _v->changed(); }
//! @brief Set variable changed state.
inline void setChanged(UInt8 changed)
{ ASMJIT_ASSERT(_v); _v->setChanged(changed); }
//! @brief Return whether variable is reusable.
inline UInt8 reusable() const ASMJIT_NOTHROW
{ ASMJIT_ASSERT(_v); return _v->reusable(); }
//! @brief Return whether variable contains custom memory home address.
inline UInt8 customMemoryHome() const ASMJIT_NOTHROW
{ ASMJIT_ASSERT(_v); return _v->customMemoryHome(); }
inline void setMemoryHome(const Mem& memoryHome) ASMJIT_NOTHROW
{ ASMJIT_ASSERT(_v); _v->setMemoryHome(memoryHome); }
//! @brief Return memory address operand.
//!
//! @note Getting memory address operand will always call @c spill().
inline const Mem& m() const
{ ASMJIT_ASSERT(_v); return _v->m(); }
// [Reference counting]
//! @brief Increase reference count and return @c Variable instance.
inline Variable* ref()
{ ASMJIT_ASSERT(_v); return _v->ref(); }
// [Custom Spill / Restore]
//! @brief Return @c true if variable uses custom alloc / spill functions.
//!
//! @sa @c AsmJit::Variable::isCustom() method.
inline bool isCustom() const { ASMJIT_ASSERT(_v); return _v->isCustom(); }
//! @brief Get custom restore function.
inline AllocFn allocFn() const { ASMJIT_ASSERT(_v); return _v->allocFn(); }
//! @brief Get custom spill function.
inline SpillFn spillFn() const { ASMJIT_ASSERT(_v); return _v->spillFn(); }
//! @brief Get custom data pointer.
inline void* dataPtr() const { ASMJIT_ASSERT(_v); return _v->dataPtr(); }
//! @brief Get custom data pointer.
inline SysInt dataInt() const { ASMJIT_ASSERT(_v); return _v->dataInt(); }
//! @brief Set custom restore function.
inline void setAllocFn(AllocFn fn) { ASMJIT_ASSERT(_v); _v->setAllocFn(fn); }
//! @brief Set custom spill function.
inline void setSpillFn(SpillFn fn) { ASMJIT_ASSERT(_v); _v->setSpillFn(fn); }
//! @brief Set custom data.
inline void setDataPtr(void* data) { ASMJIT_ASSERT(_v); _v->setDataPtr(data); }
//! @brief Set custom data.
inline void setDataInt(SysInt data) { ASMJIT_ASSERT(_v); _v->setDataInt(data); }
inline const char* name() const { ASMJIT_ASSERT(_v); return _v->name(); }
inline void setName(const char* name) const { ASMJIT_ASSERT(_v); _v->setName(name); }
inline bool operator==(const VariableRef& other) const { return _v == other._v; }
inline bool operator!=(const VariableRef& other) const { return _v != other._v; }
protected:
void _assign(const VariableRef& other)
{
Variable* tmp = other._v ? other._v->ref() : NULL;
if (_v) _v->deref();
_v = tmp;
}
//! @brief @c Variable instance.
Variable* _v;
private:
// Disable copy.
ASMJIT_DISABLE_COPY(VariableRef);
};
// Helper macro for VariableRef::... register access methods.
#define __REG_ACCESS(__regClass__, __allocType__, __preferredRegCode__, __regType__) \
ASMJIT_ASSERT(_v); \
__regClass__ result; \
_v->getReg(__allocType__, __preferredRegCode__, (BaseReg*)&result, __regType__); \
return result
//! @brief 32 bit integer variable wrapper.
struct ASMJIT_HIDDEN Int32Ref : public VariableRef
{
// [Construction / Destruction]
inline Int32Ref() : VariableRef() {}
inline Int32Ref(const Int32Ref& other) : VariableRef(other._v) {}
inline Int32Ref(Variable* v) : VariableRef(v) {}
inline Int32Ref& operator=(const Int32Ref& other) { _assign(other); return *this; }
// [Registers]
inline Register r (UInt8 pref = NO_REG) const { __REG_ACCESS(Register, VARIABLE_ALLOC_READWRITE, pref, REG_GPD); }
inline Register r8 (UInt8 pref = NO_REG) const { __REG_ACCESS(Register, VARIABLE_ALLOC_READWRITE, pref, REG_GPB); }
inline Register r16(UInt8 pref = NO_REG) const { __REG_ACCESS(Register, VARIABLE_ALLOC_READWRITE, pref, REG_GPW); }
inline Register r32(UInt8 pref = NO_REG) const { __REG_ACCESS(Register, VARIABLE_ALLOC_READWRITE, pref, REG_GPD); }
#if defined(ASMJIT_X64)
inline Register r64(UInt8 pref = NO_REG) const { __REG_ACCESS(Register, VARIABLE_ALLOC_READWRITE, pref, REG_GPQ); }
#endif // ASMJIT_X64
inline Register c (UInt8 pref = NO_REG) const { __REG_ACCESS(Register, VARIABLE_ALLOC_READ , pref, REG_GPD); }
inline Register c8 (UInt8 pref = NO_REG) const { __REG_ACCESS(Register, VARIABLE_ALLOC_READ , pref, REG_GPB); }
inline Register c16(UInt8 pref = NO_REG) const { __REG_ACCESS(Register, VARIABLE_ALLOC_READ , pref, REG_GPW); }
inline Register c32(UInt8 pref = NO_REG) const { __REG_ACCESS(Register, VARIABLE_ALLOC_READ , pref, REG_GPD); }
#if defined(ASMJIT_X64)
inline Register c64(UInt8 pref = NO_REG) const { __REG_ACCESS(Register, VARIABLE_ALLOC_READ , pref, REG_GPQ); }
#endif // ASMJIT_X64
inline Register x (UInt8 pref = NO_REG) const { __REG_ACCESS(Register, VARIABLE_ALLOC_WRITE , pref, REG_GPD); }
inline Register x8 (UInt8 pref = NO_REG) const { __REG_ACCESS(Register, VARIABLE_ALLOC_WRITE , pref, REG_GPB); }
inline Register x16(UInt8 pref = NO_REG) const { __REG_ACCESS(Register, VARIABLE_ALLOC_WRITE , pref, REG_GPW); }
inline Register x32(UInt8 pref = NO_REG) const { __REG_ACCESS(Register, VARIABLE_ALLOC_WRITE , pref, REG_GPD); }
#if defined(ASMJIT_X64)
inline Register x64(UInt8 pref = NO_REG) const { __REG_ACCESS(Register, VARIABLE_ALLOC_WRITE , pref, REG_GPQ); }
#endif // ASMJIT_X64
};
#if defined(ASMJIT_X64)
//! @brief 64 bit integer variable wrapper.
struct ASMJIT_HIDDEN Int64Ref : public VariableRef
{
// [Construction / Destruction]
inline Int64Ref() : VariableRef() {}
inline Int64Ref(const Int64Ref& other) : VariableRef(other._v) {}
inline Int64Ref(Variable* v) : VariableRef(v) {}
inline Int64Ref& operator=(const Int64Ref& other) { _assign(other); return *this; }
// [Registers]
inline Register r (UInt8 pref = NO_REG) const { __REG_ACCESS(Register, VARIABLE_ALLOC_READWRITE, pref, REG_GPQ); }
inline Register r8 (UInt8 pref = NO_REG) const { __REG_ACCESS(Register, VARIABLE_ALLOC_READWRITE, pref, REG_GPB); }
inline Register r16(UInt8 pref = NO_REG) const { __REG_ACCESS(Register, VARIABLE_ALLOC_READWRITE, pref, REG_GPW); }
inline Register r32(UInt8 pref = NO_REG) const { __REG_ACCESS(Register, VARIABLE_ALLOC_READWRITE, pref, REG_GPD); }
inline Register r64(UInt8 pref = NO_REG) const { __REG_ACCESS(Register, VARIABLE_ALLOC_READWRITE, pref, REG_GPQ); }
inline Register c (UInt8 pref = NO_REG) const { __REG_ACCESS(Register, VARIABLE_ALLOC_READ , pref, REG_GPQ); }
inline Register c8 (UInt8 pref = NO_REG) const { __REG_ACCESS(Register, VARIABLE_ALLOC_READ , pref, REG_GPB); }
inline Register c16(UInt8 pref = NO_REG) const { __REG_ACCESS(Register, VARIABLE_ALLOC_READ , pref, REG_GPW); }
inline Register c32(UInt8 pref = NO_REG) const { __REG_ACCESS(Register, VARIABLE_ALLOC_READ , pref, REG_GPD); }
inline Register c64(UInt8 pref = NO_REG) const { __REG_ACCESS(Register, VARIABLE_ALLOC_READ , pref, REG_GPQ); }
inline Register x (UInt8 pref = NO_REG) const { __REG_ACCESS(Register, VARIABLE_ALLOC_WRITE , pref, REG_GPQ); }
inline Register x8 (UInt8 pref = NO_REG) const { __REG_ACCESS(Register, VARIABLE_ALLOC_WRITE , pref, REG_GPB); }
inline Register x16(UInt8 pref = NO_REG) const { __REG_ACCESS(Register, VARIABLE_ALLOC_WRITE , pref, REG_GPW); }
inline Register x32(UInt8 pref = NO_REG) const { __REG_ACCESS(Register, VARIABLE_ALLOC_WRITE , pref, REG_GPD); }
inline Register x64(UInt8 pref = NO_REG) const { __REG_ACCESS(Register, VARIABLE_ALLOC_WRITE , pref, REG_GPQ); }
};
#endif // ASMJIT_X64
//! @brief MMX variable wrapper.
struct ASMJIT_HIDDEN MMRef : public VariableRef
{
// [Construction / Destruction]
inline MMRef() : VariableRef() {}
inline MMRef(const MMRef& other) : VariableRef(other._v) {}
inline MMRef(Variable* v) : VariableRef(v) {}
inline MMRef& operator=(const MMRef& other) { _assign(other); return *this; }
// [Registers]
inline MMRegister r(UInt8 pref = NO_REG) const { __REG_ACCESS(MMRegister, VARIABLE_ALLOC_READWRITE, pref, REG_MM); }
inline MMRegister c(UInt8 pref = NO_REG) const { __REG_ACCESS(MMRegister, VARIABLE_ALLOC_READ , pref, REG_MM); }
inline MMRegister x(UInt8 pref = NO_REG) const { __REG_ACCESS(MMRegister, VARIABLE_ALLOC_WRITE , pref, REG_MM); }
};
//! @brief SSE variable wrapper.
struct ASMJIT_HIDDEN XMMRef : public VariableRef
{
// [Construction / Destruction]
inline XMMRef() : VariableRef() {}
inline XMMRef(const XMMRef& other) : VariableRef(other._v) {}
inline XMMRef(Variable* v) : VariableRef(v) {}
inline XMMRef& operator=(const XMMRef& other) { _assign(other); return *this; }
// [Registers]
inline XMMRegister r(UInt8 pref = NO_REG) const { __REG_ACCESS(XMMRegister, VARIABLE_ALLOC_READWRITE, pref, REG_XMM); }
inline XMMRegister c(UInt8 pref = NO_REG) const { __REG_ACCESS(XMMRegister, VARIABLE_ALLOC_READ , pref, REG_XMM); }
inline XMMRegister x(UInt8 pref = NO_REG) const { __REG_ACCESS(XMMRegister, VARIABLE_ALLOC_WRITE , pref, REG_XMM); }
};
#if defined(ASMJIT_X86)
typedef Int32Ref SysIntRef;
#else
typedef Int64Ref SysIntRef;
#endif
//! @brief Pointer variable wrapper (same as system integer).
typedef SysIntRef PtrRef;
// Cleanup
#undef __REG_ACCESS
// ============================================================================
// [AsmJit::State]
// ============================================================================
//! @brief Contains informations about current register state.
//!
//! @note Always use StateRef to manage register states and don't create State
//! directly. Instead use @c AsmJit::Function::saveState() and
//! @c AsmJit::Function::restoreState() methods.
//!
//! @sa StateRef
struct ASMJIT_API State
{
// [Construction / Destruction]
State(Compiler* c, Function* f);
virtual ~State();
//! @brief State entry (variable and some details about variable life cycle).
struct Entry
{
Variable* v;
UInt32 lifeId;
UInt8 state;
UInt8 changed;
};
//! @brief State data (allocated registers <-> variables).
struct Data
{
union
{
//! @brief All variables in one array.
Entry regs[16+8+16];
struct {
//! @brief Regeral purpose registers.
Entry gp[16];
//! @brief MMX registers.
Entry mm[8];
//! @brief XMM registers.
Entry xmm[16];
};
};
//! @brief used GP registers bitmask.
UInt32 usedGpRegisters;
//! @brief used MMX registers bitmask.
UInt32 usedMmRegisters;
//! @brief used XMM registers bitmask.
UInt32 usedXmmRegisters;
};
static void saveFunctionState(Data* dst, Function* f);
private:
//! @brief Clear state.
void _clear();
//! @brief Save function state (there is no code generated when saving state).
void _save();
//! @brief Restore function state, can spill and alloc registers.
void _restore();
//! @brief Set function state to current state.
//!
//! @note This method is similar to @c _restore(), but it will not alloc or
//! spill registers.
void _set();
//! @brief Compiler this state is related to.
Compiler* _compiler;
//! @brief Function this state is related to.
Function* _function;
//! @brief State data.
Data _data;
friend struct CompilerCore;
friend struct Function;
friend struct StateRef;
// Disable copy.
ASMJIT_DISABLE_COPY(State);
};
// ============================================================================
// [AsmJit::StateRef]
// ============================================================================
//! @brief State wrapper used to manage @c State's.
struct ASMJIT_HIDDEN StateRef
{
//! @brief Create StateRef instance from @a state (usually returned by
//! @c Compiler / @c Function).
inline StateRef(State* state) :
_state(state)
{}
//! @brief Destroy StateRef instance.
inline ~StateRef();
//! @brief Return managed @c State instance.
inline State* state() const { return _state; }
//! @brief Implicit cast to @c State.
inline operator State*() const { return _state; }
private:
State* _state;
// Disable copy.
ASMJIT_DISABLE_COPY(StateRef);
};
// ============================================================================
// [AsmJit::Emittable]
// ============================================================================
//! @brief Emmitable.
//!
//! Emittable is object that can emit single or more instructions. To
//! create your custom emittable it's needed to override abstract virtual
//! method @c emit().
//!
//! When you are finished serializing instructions to the @c Compiler and you
//! call @c Compiler::make(), it will first call @c prepare() method for each
//! emittable in list, then @c emit() and then @c postEmit(). Prepare can be
//! used to calculate something that can be only calculated when everything
//! is finished (for example @c Function uses @c prepare() to relocate memory
//! home for all memory/spilled variables). @c emit() should be used to emit
//! instruction or multiple instructions into @a Assembler stream, and
//! @c postEmit() is here to allow emitting embedded data (after function
//! declaration), etc.
struct ASMJIT_API Emittable
{
// [Construction / Destruction]
//! @brief Create new emittable.
//!
//! Never create @c Emittable by @c new operator or on the stack, use
//! @c Compiler::newObject template to do that.
Emittable(Compiler* c, UInt32 type) ASMJIT_NOTHROW;
//! @brief Destroy emittable.
//!
//! @note Never destroy emittable using @c delete keyword, @c Compiler
//! manages all emittables in internal memory pool and it will destroy
//! all emittables after you destroy it.
virtual ~Emittable() ASMJIT_NOTHROW;
// [Emit]
//! @brief Prepare for emitting (optional).
virtual void prepare();
//! @brief Emit instruction stream.
virtual void emit(Assembler& a) = 0;
//! @brief Post emit (optional).
virtual void postEmit(Assembler& a);
// [Methods]
//! @brief Return compiler instance where this emittable is connected to.
inline Compiler* compiler() const ASMJIT_NOTHROW { return _compiler; }
//! @brief Return previsou emittable in list.
inline Emittable* prev() const ASMJIT_NOTHROW { return _prev; }
//! @brief Return next emittable in list.
inline Emittable* next() const ASMJIT_NOTHROW { return _next; }
//! @brief Return emittable type, see @c EMITTABLE_TYPE.
inline UInt32 type() const ASMJIT_NOTHROW { return _type; }
// [Members]
protected:
//! @brief Compiler where this emittable is connected to.
Compiler* _compiler;
//! @brief Previous emittable.
Emittable* _prev;
//! @brief Next emittable.
Emittable* _next;
//! @brief Type of emittable, see @c EMITTABLE_TYPE.
UInt32 _type;
private:
friend struct CompilerCore;
// Disable copy.
ASMJIT_DISABLE_COPY(Emittable);
};
// ============================================================================
// [AsmJit::Comment]
// ============================================================================
//! @brief Emittable used to emit comment into @c Assembler logger.
//!
//! Comments allows to comment your assembler stream for better debugging
//! and visualization whats happening. Comments are ignored if logger is not
//! set.
//!
//! Comment data can't be modified after comment was created.
struct ASMJIT_API Comment : public Emittable
{
// [Construction / Destruction]
Comment(Compiler* c, const char* str) ASMJIT_NOTHROW;
virtual ~Comment() ASMJIT_NOTHROW;
// [Emit]
virtual void emit(Assembler& a);
// [Methods]
//! @brief Get comment data.
inline const char* str() const ASMJIT_NOTHROW { return _str; }
// [Members]
private:
const char* _str;
};
// ============================================================================
// [AsmJit::EmbeddedData]
// ============================================================================
//! @brief Emittable used to emit comment into @c Assembler logger.
//!
//! @note This class is always allocated by @c AsmJit::Compiler.
struct ASMJIT_API EmbeddedData : public Emittable
{
// [Construction / Destruction]
EmbeddedData(Compiler* c, SysUInt capacity, const void* data, SysUInt size) ASMJIT_NOTHROW;
virtual ~EmbeddedData() ASMJIT_NOTHROW;
// [Emit]
virtual void emit(Assembler& a);
// [Methods]
//! @brief Get size of embedded data.
SysUInt size() const ASMJIT_NOTHROW { return _size; }
//! @brief Get capacity of embedded data.
//! @internal
SysUInt capacity() const ASMJIT_NOTHROW { return _size; }
//! @brief Get pointer to embedded data.
UInt8* data() const ASMJIT_NOTHROW { return (UInt8*)_data; }
// [Members]
private:
SysUInt _size;
SysUInt _capacity;
UInt8 _data[sizeof(void*)];
friend struct CompilerCore;
};
// ============================================================================
// [AsmJit::Align]
// ============================================================================
//! @brief Emittable used to align assembler code.
struct ASMJIT_API Align : public Emittable
{
// [Construction / Destruction]
Align(Compiler* c, SysInt size = 0) ASMJIT_NOTHROW;
virtual ~Align() ASMJIT_NOTHROW;
// [Methods]
virtual void emit(Assembler& a);
//! @brief Get align size in bytes.
inline SysInt size() const ASMJIT_NOTHROW { return _size; }
//! @brief Set align size in bytes to @a size.
inline void setSize(SysInt size) ASMJIT_NOTHROW { _size = size; }
private:
SysInt _size;
};
// ============================================================================
// [AsmJit::Instruction]
// ============================================================================
//! @brief Emittable that represents single instruction and its operands.
struct ASMJIT_API Instruction : public Emittable
{
// [Construction / Destruction]
Instruction(Compiler* c) ASMJIT_NOTHROW;
Instruction(Compiler* c,
UInt32 code,
const Operand* o1, const Operand* o2, const Operand* o3,
const char* inlineComment = NULL) ASMJIT_NOTHROW;
virtual ~Instruction() ASMJIT_NOTHROW;
// [Emit]
virtual void emit(Assembler& a);
// [Methods]
//! @brief Return instruction code, see @c INST_CODE.
inline UInt32 code() const ASMJIT_NOTHROW { return _code; }
//! @brief Return array of operands (3 operands total).
inline Operand* const* ops() ASMJIT_NOTHROW { return _o; }
//! @brief Return first instruction operand.
inline Operand* o1() const ASMJIT_NOTHROW { return _o[0]; }
//! @brief Return second instruction operand.
inline Operand* o2() const ASMJIT_NOTHROW { return _o[1]; }
//! @brief Return third instruction operand.
inline Operand* o3() const ASMJIT_NOTHROW { return _o[2]; }
//! @brief Set instruction code.
//!
//! Please do not modify instruction code if you are not know what you are
//! doing. Incorrect instruction code or operands can assert() in runtime.
inline void setCode(UInt32 code) ASMJIT_NOTHROW { _code = code; }
// [Members]
private:
//! @brief Instruction code, see @c INST_CODE.
UInt32 _code;
//! @brief Instruction operands.
Operand *_o[3];
//! @brief Static array for instruction operands (cache)
Operand _ocache[3];
const char* _inlineComment;
friend struct Function;
};
// ============================================================================
// [AsmJit::TypeAsId]
// ============================================================================
//! @brief Template based type to variable ID converter.
template<typename T>
struct TypeAsId
{
#if defined(ASMJIT_NODOC)
enum {
//! @brief Variable id, see @c VARIABLE_TYPE enum
Id = X
};
#endif
};
// Skip documenting this.
#if !defined(ASMJIT_NODOC)
template<typename T>
struct TypeAsId<T*> { enum { Id = VARIABLE_TYPE_PTR }; };
#define ASMJIT_DECLARE_TYPE_AS_ID(__T__, __Id__) \
template<> \
struct TypeAsId<__T__> { enum { Id = __Id__ }; }
ASMJIT_DECLARE_TYPE_AS_ID(Int32, VARIABLE_TYPE_INT32);
ASMJIT_DECLARE_TYPE_AS_ID(UInt32, VARIABLE_TYPE_UINT32);
#if defined(ASMJIT_X64)
ASMJIT_DECLARE_TYPE_AS_ID(Int64, VARIABLE_TYPE_INT64);
ASMJIT_DECLARE_TYPE_AS_ID(UInt64, VARIABLE_TYPE_UINT64);
#endif // ASMJIT_X64
ASMJIT_DECLARE_TYPE_AS_ID(float, VARIABLE_TYPE_FLOAT);
ASMJIT_DECLARE_TYPE_AS_ID(double, VARIABLE_TYPE_DOUBLE);
#endif // !ASMJIT_NODOC
// ============================================================================
// [AsmJit::Function Builder]
// ============================================================================
//! @brief Class used to build function without arguments.
struct BuildFunction0
{
inline const UInt32* args() const ASMJIT_NOTHROW { return NULL; }
inline SysUInt count() const ASMJIT_NOTHROW { return 0; }
};
//! @brief Class used to build function with 1 argument.
template<typename P0>
struct BuildFunction1
{
inline const UInt32* args() const ASMJIT_NOTHROW { static const UInt32 data[] = { TypeAsId<P0>::Id }; return data; }
inline SysUInt count() const ASMJIT_NOTHROW { return 1; }
};
//! @brief Class used to build function with 2 arguments.
template<typename P0, typename P1>
struct BuildFunction2
{
inline const UInt32* args() const ASMJIT_NOTHROW { static const UInt32 data[] = { TypeAsId<P0>::Id, TypeAsId<P1>::Id }; return data; }
inline SysUInt count() const ASMJIT_NOTHROW { return 2; }
};
//! @brief Class used to build function with 3 arguments.
template<typename P0, typename P1, typename P2>
struct BuildFunction3
{
inline const UInt32* args() const ASMJIT_NOTHROW { static const UInt32 data[] = { TypeAsId<P0>::Id, TypeAsId<P1>::Id, TypeAsId<P2>::Id }; return data; }
inline SysUInt count() const ASMJIT_NOTHROW { return 3; }
};
//! @brief Class used to build function with 4 arguments.
template<typename P0, typename P1, typename P2, typename P3>
struct BuildFunction4
{
inline const UInt32* args() const ASMJIT_NOTHROW { static const UInt32 data[] = { TypeAsId<P0>::Id, TypeAsId<P1>::Id, TypeAsId<P2>::Id, TypeAsId<P3>::Id }; return data; }
inline SysUInt count() const ASMJIT_NOTHROW { return 4; }
};
//! @brief Class used to build function with 5 arguments.
template<typename P0, typename P1, typename P2, typename P3, typename P4>
struct BuildFunction5
{
inline const UInt32* args() const ASMJIT_NOTHROW { static const UInt32 data[] = { TypeAsId<P0>::Id, TypeAsId<P1>::Id, TypeAsId<P2>::Id, TypeAsId<P3>::Id, TypeAsId<P4>::Id }; return data; }
inline SysUInt count() const ASMJIT_NOTHROW { return 5; }
};
//! @brief Class used to build function with 6 arguments.
template<typename P0, typename P1, typename P2, typename P3, typename P4, typename P5>
struct BuildFunction6
{
inline const UInt32* args() const ASMJIT_NOTHROW { static const UInt32 data[] = { TypeAsId<P0>::Id, TypeAsId<P1>::Id, TypeAsId<P2>::Id, TypeAsId<P3>::Id, TypeAsId<P4>::Id, TypeAsId<P5>::Id }; return data; }
inline SysUInt count() const ASMJIT_NOTHROW { return 6; }
};
//! @brief Class used to build function with 7 arguments.
template<typename P0, typename P1, typename P2, typename P3, typename P4, typename P5, typename P6>
struct BuildFunction7
{
inline const UInt32* args() const ASMJIT_NOTHROW { static const UInt32 data[] = { TypeAsId<P0>::Id, TypeAsId<P1>::Id, TypeAsId<P2>::Id, TypeAsId<P3>::Id, TypeAsId<P4>::Id, TypeAsId<P5>::Id, TypeAsId<P6>::Id }; return data; }
inline SysUInt count() const ASMJIT_NOTHROW { return 7; }
};
//! @brief Class used to build function with 8 arguments.
template<typename P0, typename P1, typename P2, typename P3, typename P4, typename P5, typename P6, typename P7>
struct BuildFunction8
{
inline const UInt32* args() const ASMJIT_NOTHROW { static const UInt32 data[] = { TypeAsId<P0>::Id, TypeAsId<P1>::Id, TypeAsId<P2>::Id, TypeAsId<P3>::Id, TypeAsId<P4>::Id, TypeAsId<P5>::Id, TypeAsId<P6>::Id, TypeAsId<P7>::Id }; return data; }
inline SysUInt count() const ASMJIT_NOTHROW { return 8; }
};
//! @brief Class used to build function with 9 arguments.
template<typename P0, typename P1, typename P2, typename P3, typename P4, typename P5, typename P6, typename P7, typename P8>
struct BuildFunction9
{
inline const UInt32* args() const ASMJIT_NOTHROW { static const UInt32 data[] = { TypeAsId<P0>::Id, TypeAsId<P1>::Id, TypeAsId<P2>::Id, TypeAsId<P3>::Id, TypeAsId<P4>::Id, TypeAsId<P5>::Id, TypeAsId<P6>::Id, TypeAsId<P7>::Id, TypeAsId<P8>::Id }; return data; }
inline SysUInt count() const ASMJIT_NOTHROW { return 9; }
};
//! @brief Class used to build function with 9 arguments.
template<typename P0, typename P1, typename P2, typename P3, typename P4, typename P5, typename P6, typename P7, typename P8, typename P9>
struct BuildFunction10
{
inline const UInt32* args() const ASMJIT_NOTHROW { static const UInt32 data[] = { TypeAsId<P0>::Id, TypeAsId<P1>::Id, TypeAsId<P2>::Id, TypeAsId<P3>::Id, TypeAsId<P4>::Id, TypeAsId<P5>::Id, TypeAsId<P6>::Id, TypeAsId<P7>::Id, TypeAsId<P8>::Id, TypeAsId<P9>::Id }; return data; }
inline SysUInt count() const ASMJIT_NOTHROW { return 10; }
};
// ============================================================================
// [AsmJit::Function]
// ============================================================================
//! @brief Function emittable used to generate C/C++ functions.
//!
//! Functions are base blocks for generating assembler output. Each generated
//! assembler stream needs standard entry and leave sequences thats compatible
//! to operating system conventions (ABI).
//!
//! Function class can be used to generate entry (prolog) and leave (epilog)
//! sequences that is compatible to a given calling convention and to allocate
//! and manage variables that can be allocated to registers or spilled.
//!
//! @note To create function use @c AsmJit::Compiler::newFunction() method, do
//! not create @c Function instances by different ways.
//!
//! @sa @c State, @c StateRef, @c Variable, @c VariableRef.
struct ASMJIT_API Function : public Emittable
{
// --------------------------------------------------------------------------
// [Construction / Destruction]
// --------------------------------------------------------------------------
//! @brief Create new @c Function instance.
//!
//! @note Always use @c AsmJit::Compiler::newFunction() to create @c Function
//! instance.
Function(Compiler* c) ASMJIT_NOTHROW;
//! @brief Destroy @c Function instance.
virtual ~Function() ASMJIT_NOTHROW;
// --------------------------------------------------------------------------
// [Emit]
// --------------------------------------------------------------------------
virtual void prepare();
virtual void emit(Assembler& a);
// --------------------------------------------------------------------------
// [Calling Convention / Function Arguments]
// --------------------------------------------------------------------------
//! @brief Set function prototype.
//!
//! This will set function calling convention and setup arguments variables.
void setPrototype(UInt32 cconv, const UInt32* args, SysUInt count);
//! @brief Set naked function to true or false (naked means no prolog / epilog code).
void setNaked(UInt8 naked) ASMJIT_NOTHROW;
//! @brief Enable or disable allocation of EBP/RBP register.
inline void setAllocableEbp(UInt8 val) ASMJIT_NOTHROW { _allocableEbp = val; }
//! @brief Enable or disable allocation of EBP/RBP register.
inline void setPrologEpilogPushPop(UInt8 val) ASMJIT_NOTHROW { _prologEpilogPushPop = val; }
//! @brief Enable or disable emms instruction in epilog.
inline void setEmms(UInt8 val) ASMJIT_NOTHROW { _emms = val; }
//! @brief Enable or disable sfence instruction in epilog.
inline void setSfence(UInt8 val) ASMJIT_NOTHROW { _sfence = val; }
//! @brief Enable or disable lfence instruction in epilog.
inline void setLfence(UInt8 val) ASMJIT_NOTHROW { _lfence = val; }
//! @brief Return whether optimizing prolog / epilog is enabled or disabled.
inline void setOptimizedPrologEpilog(UInt8 val) ASMJIT_NOTHROW { _optimizedPrologEpilog = val; }
//! @brief Return function calling convention, see @c CALL_CONV.
inline UInt32 cconv() const ASMJIT_NOTHROW { return _cconv; }
//! @brief Return @c true if callee pops the stack by ret() instruction.
//!
//! Stdcall calling convention is designed to pop the stack by callee,
//! but all calling conventions used in MSVC extept cdecl does that.
//!
//! @note This is related to used calling convention, it's not affected by
//! number of function arguments or their types.
inline UInt8 calleePopsStack() const ASMJIT_NOTHROW { return _calleePopsStack; }
//! @brief Return @c true if function is naked (no prolog / epilog code).
inline UInt8 naked() const ASMJIT_NOTHROW { return _naked; }
//! @brief Return @c true if EBP/RBP register can be allocated by register allocator.
inline UInt8 allocableEbp() const ASMJIT_NOTHROW { return _allocableEbp; }
//! @brief Return @c true if EBP/RBP register can be allocated by register allocator.
inline UInt8 prologEpilogPushPop() const ASMJIT_NOTHROW { return _prologEpilogPushPop; }
//! @brief Return whether emms instruction is enabled or disabled in epilog.
inline UInt8 emms() const ASMJIT_NOTHROW { return _emms; }
//! @brief Return whether sfence instruction is enabled or disabled in epilog.
inline UInt8 sfence() const ASMJIT_NOTHROW { return _sfence; }
//! @brief Return whether lfence instruction is enabled or disabled in epilog.
inline UInt8 lfence() const ASMJIT_NOTHROW { return _lfence; }
//! @brief Return whether optimizing prolog / epilog is enabled or disabled.
inline UInt8 optimizedPrologEpilog() const ASMJIT_NOTHROW { return _optimizedPrologEpilog; }
//! @brief Return direction of arguments passed on the stack.
//!
//! Direction should be always @c ARGUMENT_DIR_RIGHT_TO_LEFT.
//!
//! @note This is related to used calling convention, it's not affected by
//! number of function arguments or their types.
inline UInt32 cconvArgumentsDirection() const ASMJIT_NOTHROW { return _cconvArgumentsDirection; }
//! @brief Return registers used to pass first integer parameters by current
//! calling convention.
//!
//! @note This is related to used calling convention, it's not affected by
//! number of function arguments or their types.
inline const UInt32* cconvArgumentsGp() const ASMJIT_NOTHROW { return _cconvArgumentsGp; }
//! @brief Return registers used to pass first SP-FP or DP-FPparameters by
//! current calling convention.
//!
//! @note This is related to used calling convention, it's not affected by
//! number of function arguments or their types.
inline const UInt32* cconvArgumentsXmm() const ASMJIT_NOTHROW { return _cconvArgumentsXmm; }
//! @brief Return bitmask of general purpose registers that's preserved
//! (non-volatile).
//!
//! @note This is related to used calling convention, it's not affected by
//! number of function arguments or their types.
inline UInt32 cconvPreservedGp() const ASMJIT_NOTHROW { return _cconvPreservedGp; }
//! @brief Return bitmask of sse registers that's preserved (non-volatile).
//!
//! @note This is related to used calling convention, it's not affected by
//! number of function arguments or their types.
inline UInt32 cconvPreservedXmm() const ASMJIT_NOTHROW { return _cconvPreservedXmm; }
// --------------------------------------------------------------------------
// [Registers allocator / Variables]
// --------------------------------------------------------------------------
//! @brief Return argument at @a i.
inline Variable* argument(SysInt i) ASMJIT_NOTHROW
{
ASMJIT_ASSERT((SysUInt)i < _argumentsCount);
return _variables[i];
}
//! @brief Create new variable
Variable* newVariable(UInt8 type, UInt8 priority = 10, UInt8 preferredRegister = NO_REG);
bool alloc(Variable* v,
UInt8 mode = VARIABLE_ALLOC_READWRITE,
UInt8 preferredRegister = NO_REG);
bool spill(Variable* v);
void unuse(Variable* v);
void spillAll();
void spillAllGp();
void spillAllMm();
void spillAllXmm();
void _spillAll(SysUInt start, SysUInt end);
void spillRegister(const BaseReg& reg);
//! @brief Get count of free GP registers (in current state).
SysInt numFreeGp() const;
//! @brief Get count of free MMX registers (in current state).
SysInt numFreeMm() const;
//! @brief Get count of free XMM registers (in current state).
SysInt numFreeXmm() const;
//! @brief Get if variable @a v is prevented.
//! @return true if variable @a v is prevented (in _prevented list).
//! @sa _prevented.
bool isPrevented(Variable* v);
//! @brief Add variable @a v to list of prevented variables.
//! @sa _prevented.
void addPrevented(Variable* v);
//! @brief Remove variable @a v from list of prevented variables.
//! @sa _prevented.
void removePrevented(Variable* v);
//! @brief Remove all variables from list of prevented ones.
//! @sa _prevented.
void clearPrevented();
//! @brief Return spill candidate for variable type @a type.
Variable* _getSpillCandidate(UInt8 type);
void _allocAs(Variable* v, UInt8 mode, UInt32 code);
void _allocReg(UInt8 code, Variable* v);
void _freeReg(UInt8 code);
void _moveGp(Variable* v, UInt8 code);
void _exchangeGp(Variable* v, UInt8 mode, Variable* other);
void _postAlloc(Variable* v, UInt8 mode);
//! @brief Return size of alignment on the stack.
//!
//! Stack is aligned to 16 bytes by default. For X64 platforms there is
//! no extra code needed to align stack to 16 bytes, because it's default
//! stack alignment.
inline SysInt stackAlignmentSize() const ASMJIT_NOTHROW { return _stackAlignmentSize; }
//! @brief Size needed to save registers in prolog / epilog.
inline SysInt prologEpilogStackSize() const ASMJIT_NOTHROW { return _prologEpilogStackSize; }
//! @brief Return size of variables on the stack.
//!
//! This variable is always aligned to 16 bytes for each platform.
inline SysInt variablesStackSize() const ASMJIT_NOTHROW { return _variablesStackSize; }
//! @brief Return count of arguments.
inline UInt32 argumentsCount() const ASMJIT_NOTHROW { return _argumentsCount; }
//! @brief Return stack size of all function arguments (passed on the
//! stack).
inline UInt32 argumentsStackSize() const ASMJIT_NOTHROW { return _argumentsStackSize; }
//! @brief Return bitmask of all used (for actual context) general purpose registers.
inline UInt32 usedGpRegisters() const ASMJIT_NOTHROW { return _usedGpRegisters; }
//! @brief Return bitmask of all used (for actual context) mmx registers.
inline UInt32 usedMmRegisters() const ASMJIT_NOTHROW { return _usedMmRegisters; }
//! @brief Return bitmask of all used (for actual context) sse registers.
inline UInt32 usedXmmRegisters() const ASMJIT_NOTHROW { return _usedXmmRegisters; }
//! @brief Mark general purpose registers in the given @a mask as used.
inline void useGpRegisters(UInt32 mask) ASMJIT_NOTHROW { _usedGpRegisters |= mask; }
//! @brief Mark mmx registers in the given @a mask as used.
inline void useMmRegisters(UInt32 mask) ASMJIT_NOTHROW { _usedMmRegisters |= mask; }
//! @brief Mark sse registers in the given @a mask as used.
inline void useXmmRegisters(UInt32 mask) ASMJIT_NOTHROW { _usedXmmRegisters |= mask; }
//! @brief Mark general purpose registers in the given @a mask as unused.
inline void unuseGpRegisters(UInt32 mask) ASMJIT_NOTHROW { _usedGpRegisters &= ~mask; }
//! @brief Mark mmx registers in the given @a mask as unused.
inline void unuseMmRegisters(UInt32 mask) ASMJIT_NOTHROW { _usedMmRegisters &= ~mask; }
//! @brief Mark sse registers in the given @a mask as unused.
inline void unuseXmmRegisters(UInt32 mask) ASMJIT_NOTHROW { _usedXmmRegisters &= ~mask; }
//! @brief Return bitmask of all changed general purpose registers during
//! function execution (for generating optimized prolog / epilog).
inline UInt32 modifiedGpRegisters() const ASMJIT_NOTHROW { return _modifiedGpRegisters; }
//! @brief Return bitmask of all changed mmx registers during
//! function execution (for generating optimized prolog / epilog).
inline UInt32 modifiedMmRegisters() const ASMJIT_NOTHROW { return _modifiedMmRegisters; }
//! @brief Return bitmask of all changed sse registers during
//! function execution (for generating optimized prolog / epilog).
inline UInt32 modifiedXmmRegisters() const ASMJIT_NOTHROW { return _modifiedXmmRegisters; }
//! @brief Mark general purpose registers in the given @a mask as modified.
inline void modifyGpRegisters(UInt32 mask) ASMJIT_NOTHROW { _modifiedGpRegisters |= mask; }
//! @brief Mark mmx registers in the given @a mask as modified.
inline void modifyMmRegisters(UInt32 mask) ASMJIT_NOTHROW { _modifiedMmRegisters |= mask; }
//! @brief Mark sse registers in the given @a mask as modified.
inline void modifyXmmRegisters(UInt32 mask) ASMJIT_NOTHROW { _modifiedXmmRegisters |= mask; }
//! @brief Get count of GP registers that must be saved by prolog and restored
//! by epilog.
SysInt countOfGpRegistersToBeSaved() const ASMJIT_NOTHROW;
//! @brief Get count of XMM registers that must be saved by prolog and restored
//! by epilog.
SysInt countOfXmmRegistersToBeSaved() const ASMJIT_NOTHROW;
// --------------------------------------------------------------------------
// [State]
// --------------------------------------------------------------------------
//! @brief Save function current register state.
//!
//! To save function state always wrap returned value into @c StateRef:
//!
//! @code
//! // Your function
//! Function &f = ...;
//!
//! // Block
//! {
//! // Save state
//! StateRef state(f.saveState());
//!
//! // Your code ...
//!
//! // Restore state (automatic by @c StateRef destructor).
//! }
//!
//! @endcode
State* saveState();
//! @brief Restore function register state to @a state.
//! @sa saveState().
void restoreState(State* state);
//! @brief Set function register state to @a state.
void setState(State* state);
// --------------------------------------------------------------------------
// [Labels]
// --------------------------------------------------------------------------
//! @brief Return function entry label.
//!
//! Entry label can be used to call this function from another code that's
//! being generated.
inline Label* entryLabel() const ASMJIT_NOTHROW { return _entryLabel; }
//! @brief Return prolog label (label after function prolog)
inline Label* prologLabel() const ASMJIT_NOTHROW { return _prologLabel; }
//! @brief Return exit label.
//!
//! Use exit label to jump to function epilog.
inline Label* exitLabel() const ASMJIT_NOTHROW { return _exitLabel; }
private:
// --------------------------------------------------------------------------
// [Calling Convention / Function Arguments]
// --------------------------------------------------------------------------
//! @brief Sets function calling convention.
void _setCallingConvention(UInt32 cconv) ASMJIT_NOTHROW;
//! @brief Sets function arguments (must be done after correct calling
//! convention is set).
void _setArguments(const UInt32* args, SysUInt len);
//! @brief Internal, used from other _jmpAndRestore method. This method does
//! the main job.
static void _jmpAndRestore(Compiler* c, Label* label);
//! @brief Calling convention, see @c CALL_CONV.
UInt32 _cconv;
//! @brief Callee pops stack;
UInt8 _calleePopsStack;
//! @brief Generate naked function?
UInt8 _naked;
//! @brief Whether EBP/RBP register can be used by register allocator.
UInt8 _allocableEbp;
//! @brief Whether Prolog and epilog should be generated by push/pop
//! instructions instead of mov instructions.
UInt8 _prologEpilogPushPop;
//! @brief Whether to generate emms instruction in epilog.
UInt8 _emms;
//! @brief Whether to generate sfence instruction in epilog.
UInt8 _sfence;
//! @brief Whether to generate lfence instruction in epilog.
UInt8 _lfence;
//! @brief Whether to optimize prolog / epilog sequences.
UInt8 _optimizedPrologEpilog;
//! @brief Direction for arguments passed on stack, see @c ARGUMENT_DIR.
UInt32 _cconvArgumentsDirection;
//! @brief List of registers that's used for first INT arguments instead of stack.
UInt32 _cconvArgumentsGp[16];
//! @brief List of registers that's used for first FP arguments instead of stack.
UInt32 _cconvArgumentsXmm[16];
//! @brief Bitmask for preserved general purpose registers.
UInt32 _cconvPreservedGp;
//! @brief Bitmask for preserved sse registers.
UInt32 _cconvPreservedXmm;
//! @brief Count of arguments (in @c _argumentsList).
UInt32 _argumentsCount;
//! @brief Count of bytes consumed by arguments on the stack.
UInt32 _argumentsStackSize;
// --------------------------------------------------------------------------
// [Registers allocator / Variables]
// --------------------------------------------------------------------------
//! @brief Size of maximum alignment size on the stack.
SysInt _stackAlignmentSize;
//! @brief Size of prolog/epilog on the stack.
SysInt _prologEpilogStackSize;
//! @brief Size of all variables on the stack.
SysInt _variablesStackSize;
//! @brief Bitmask where are stored are used GP registers.
UInt32 _usedGpRegisters;
//! @brief Bitmask where are stored are used MMX registers.
UInt32 _usedMmRegisters;
//! @brief Bitmask where are stored are used XMM registers.
UInt32 _usedXmmRegisters;
//! @brief Bitmask where are stored are modified GP registers.
UInt32 _modifiedGpRegisters;
//! @brief Bitmask where are stored are modified MMX registers.
UInt32 _modifiedMmRegisters;
//! @brief Bitmask where are stored are modified XMM registers.
UInt32 _modifiedXmmRegisters;
//! @brief List of variables managed by Function/Compiler.
PodVector<Variable*> _variables;
//! @brief List of prevented variables that can't be spilled.
//!
//! Prevented variables are variables used when generating code chain. First
//! the variables are allocated/spilled and then the assembler instruction
//! is emitted.
//!
//! For example look at this simple code:
//!
//! @code
//! Compiler::mov(dst_variable.x(), src_variable.c());
//! @endcode
//!
//! Before mov instruction is emitted, variables dst_variable and src_variable
//! are stored in @c Compiler::_prevented list. Prevention prevents variables
//! to be spilled (if some variable needs to be allocated and compiler must
//! decide which variable to spill).
PodVector<Variable*> _prevented;
//! @brief Whether to use registers prevention. This is internally turned
//! on / off while switching between states.
//!
//! Never modify this varialbe unless you know what you are doing. Prevention
//! is Compiler/Function implementation detail.
bool _usePrevention;
// --------------------------------------------------------------------------
// [State]
// --------------------------------------------------------------------------
//! @brief This is similar to State::Data, but we need to save only allocated
//! variables (this is enough). First idea was to use State here, but source
//! code was bloated by it (setting and restoring values that weren't needed)
union StateData
{
//! @brief All variables in one array.
Variable* regs[16+8+16];
struct {
//! @brief Regeral purpose registers.
Variable* gp[16];
//! @brief MMX registers.
Variable* mm[8];
//! @brief XMM registers.
Variable* xmm[16];
};
};
//! @brief Current state data.
StateData _state;
// --------------------------------------------------------------------------
// [Labels]
// --------------------------------------------------------------------------
//! @brief Function entry point label.
Label* _entryLabel;
//! @brief Label that points to start of function prolog generated by @c Prolog.
Label* _prologLabel;
//! @brief Label that points before function epilog generated by @c Epilog.
Label* _exitLabel;
friend struct CompilerCore;
friend struct State;
};
// Inlines that uses AsmJit::Function
inline bool Variable::alloc(UInt8 mode, UInt8 preferredRegister)
{ return function()->alloc(this, mode, preferredRegister); }
inline bool Variable::spill()
{ return function()->spill(this); }
inline void Variable::unuse()
{ function()->unuse(this); }
inline StateRef::~StateRef()
{ if (_state) _state->_function->restoreState(_state); }
// ============================================================================
// [AsmJit::Prolog]
// ============================================================================
//! @brief Prolog emittable.
struct ASMJIT_API Prolog : public Emittable
{
// [Construction / Destruction]
Prolog(Compiler* c, Function* f) ASMJIT_NOTHROW;
virtual ~Prolog() ASMJIT_NOTHROW;
// [Emit]
virtual void emit(Assembler& a);
// [Methods]
//! @brief Get function associated with this prolog.
inline Function* function() const ASMJIT_NOTHROW { return _function; }
// [Members]
private:
Function* _function;
Label* _label;
friend struct CompilerCore;
friend struct Function;
};
// ============================================================================
// [AsmJit::Epilog]
// ============================================================================
//! @brief Epilog emittable.
struct ASMJIT_API Epilog : public Emittable
{
// [Construction / Destruction]
Epilog(Compiler* c, Function* f) ASMJIT_NOTHROW;
virtual ~Epilog() ASMJIT_NOTHROW;
// [Emit]
virtual void emit(Assembler& a);
// [Methods]
//! @brief Get function associated with this epilog.
inline Function* function() const ASMJIT_NOTHROW { return _function; }
// [Members]
private:
Function* _function;
Label* _label;
friend struct CompilerCore;
friend struct Function;
};
// ============================================================================
// [AsmJit::Target]
// ============================================================================
//! @brief Target.
//!
//! Target is bound label location.
struct ASMJIT_API Target : public Emittable
{
// [Construction / Destruction]
Target(Compiler* c, Label* target) ASMJIT_NOTHROW;
virtual ~Target() ASMJIT_NOTHROW;
// [Emit]
virtual void emit(Assembler& a);
// [Methods]
//! @brief Return label bound to this target.
inline Label* target() const ASMJIT_NOTHROW { return _target; }
// [Members]
private:
Label* _target;
};
// ============================================================================
// [AsmJit::JumpTable]
// ============================================================================
//! @brief Jump table.
struct ASMJIT_API JumpTable : public Emittable
{
// [Construction / Destruction]
JumpTable(Compiler* c) ASMJIT_NOTHROW;
virtual ~JumpTable() ASMJIT_NOTHROW;
// [Emit]
virtual void emit(Assembler& a);
virtual void postEmit(Assembler& a);
// [Methods]
//! @brief Return target label where are informations about jump adresses.
inline Label* target() const ASMJIT_NOTHROW { return _target; }
//! @brief Return labels list.
PodVector<Label*>& labels() ASMJIT_NOTHROW { return _labels; }
//! @brief Return labels list.
const PodVector<Label*>& labels() const ASMJIT_NOTHROW { return _labels; }
//! @brief Add new label @a target to jump table.
//! @param target @c Label to add (or NULL to create one).
//! @param pos Position in jump table where to add it
Label* addLabel(Label* target = NULL, SysInt pos = -1);
// [Members]
private:
Label* _target;
PodVector<Label*> _labels;
};
// ============================================================================
// [AsmJit::CompilerCore]
// ============================================================================
//! @brief Compiler core.
//!
//! @sa @c AsmJit::Compiler.
struct ASMJIT_API CompilerCore : public Serializer
{
// -------------------------------------------------------------------------
// [Typedefs]
// -------------------------------------------------------------------------
//! @brief List of variables used and managed by @c Compiler.
typedef PodVector<Variable*> VariableList;
//! @brief List of operands used and managed by @c Compiler.
typedef PodVector<Operand*> OperandList;
// -------------------------------------------------------------------------
// [Construction / Destruction]
// -------------------------------------------------------------------------
//! @brief Create new (empty) instance of @c Compiler.
CompilerCore() ASMJIT_NOTHROW;
//! @brief Destroy @c Compiler instance.
virtual ~CompilerCore() ASMJIT_NOTHROW;
// -------------------------------------------------------------------------
// [Compiler]
// -------------------------------------------------------------------------
//! @brief Clear everything, but not deallocate buffers.
//!
//! @note This method will destroy your code.
void clear() ASMJIT_NOTHROW;
//! @brief Free internal buffer, all emitters and NULL all pointers.
//!
//! @note This method will destroy your code.
void free() ASMJIT_NOTHROW;
// -------------------------------------------------------------------------
// [Emittables]
// -------------------------------------------------------------------------
//! @brief Return first emittables in double linked list.
inline Emittable* firstEmittable() const ASMJIT_NOTHROW { return _first; }
//! @brief Return last emittable in double linked list.
inline Emittable* lastEmittable() const ASMJIT_NOTHROW { return _last; }
//! @brief Return current emittable after all emittables are emitter.
//!
//! @note If this method return @c NULL it means first position.
inline Emittable* currentEmittable() const ASMJIT_NOTHROW { return _current; }
//! @brief Add emittable after current and set current to @a emittable.
void addEmittable(Emittable* emittable) ASMJIT_NOTHROW;
//! @brief Remove emittable (and if needed set current to previous).
void removeEmittable(Emittable* emittable) ASMJIT_NOTHROW;
//! @brief Set new current emittable and return previous one.
Emittable* setCurrentEmittable(Emittable* current) ASMJIT_NOTHROW;
// -------------------------------------------------------------------------
// [Logging]
// -------------------------------------------------------------------------
//! @brief Emit a single comment line into @c Assembler logger.
//!
//! Emitting comments are useful to log something. Because assembler can be
//! generated from AST or other data structures, you may sometimes need to
//! log data characteristics or statistics.
//!
//! @note Emitting comment is not directly sent to logger, but instead it's
//! stored in @c AsmJit::Compiler and emitted when @c serialize() method is
//! called with all instructions together in correct order.
void comment(const char* fmt, ...) ASMJIT_NOTHROW;
// -------------------------------------------------------------------------
// [Function Builder]
// -------------------------------------------------------------------------
//! @brief Create a new function.
//!
//! @param cconv Calling convention to use (see @c CALL_CONV enum)
//! @param params Function arguments prototype.
//!
//! This method is usually used as a first step when generating functions
//! by @c Compiler. First parameter @a cconv specifies function calling
//! convention to use. Second parameter @a params specifies function
//! arguments. To create function arguments are used templates
//! @c BuildFunction0<>, @c BuildFunction1<...>, @c BuildFunction2<...>,
//! etc...
//!
//! Templates with BuildFunction prefix are used to generate argument IDs
//! based on real C++ types. See next example how to generate function with
//! two 32 bit integer arguments.
//!
//! @code
//! // Building function using AsmJit::Compiler example.
//!
//! // Compiler instance
//! Compiler c;
//!
//! // Begin of function (also emits function @c Prolog)
//! Function& f = *c.newFunction(
//! // Default calling convention (32 bit cdecl or 64 bit for host OS)
//! CALL_CONV_DEFAULT,
//! // Using function builder to generate arguments list
//! BuildFunction2<int, int>());
//!
//! // End of function (also emits function @c Epilog)
//! c.endFunction();
//! @endcode
//!
//! You can see that building functions is really easy. Previous code snipped
//! will generate code for function with two 32 bit integer arguments. You
//! can access arguments by @c AsmJit::Function::argument() method. Arguments
//! are indexed from 0 (like everything in C).
//!
//! @code
//! // Accessing function arguments through AsmJit::Function example.
//!
//! // Compiler instance
//! Compiler c;
//!
//! // Begin of function (also emits function @c Prolog)
//! Function& f = *c.newFunction(
//! // Default calling convention (32 bit cdecl or 64 bit for host OS)
//! CALL_CONV_DEFAULT,
//! // Using function builder to generate arguments list
//! BuildFunction2<int, int>());
//!
//! // Arguments are like other variables, you need to reference them by
//! // VariableRef types:
//! Int32Ref a0 = f.argument(0);
//! Int32Ref a1 = f.argument(1);
//!
//! // To allocate them to registers just use .alloc(), .r(), .x() or .c()
//! // variable methods:
//! c.add(a0.r(), a1.r());
//!
//! // End of function (also emits function @c Epilog)
//! c.endFunction();
//! @endcode
//!
//! Arguments are like variables. How to manipulate with variables is
//! documented in @c AsmJit::Compiler detail and @c AsmJit::VariableRef
//! class.
//!
//! @note To get current function use @c currentFunction() method or save
//! pointer to @c AsmJit::Function returned by @c AsmJit::Compiler::newFunction<>
//! method. Recommended is to save the pointer.
//!
//! @sa @c BuildFunction0, @c BuildFunction1, @c BuildFunction2, ...
template<typename T>
Function* newFunction(UInt32 cconv, const T& params) ASMJIT_NOTHROW
{ return newFunction_(cconv, params.args(), params.count()); }
//! @brief Create a new function (low level version).
//!
//! @param cconv Function calling convention (see @c AsmJit::CALL_CONV).
//! @param args Function arguments (see @c AsmJit::VARIABLE_TYPE).
//! @param count Arguments count.
//!
//! This method is internally called from @c newFunction() method and
//! contains arguments thats used internally by @c AsmJit::Compiler.
//!
//! @note To get current function use @c currentFunction() method.
Function* newFunction_(UInt32 cconv, const UInt32* args, SysUInt count) ASMJIT_NOTHROW;
//! @brief Ends current function.
Function* endFunction() ASMJIT_NOTHROW;
//! @brief Return current function.
//!
//! This method can be called within @c newFunction() and @c endFunction()
//! block to get current function you are working with. It's recommended
//! to store @c AsmJit::Function pointer returned by @c newFunction<> method,
//! because this allows you in future implement function sections outside of
//! function itself (yeah, this is possible!).
inline Function* currentFunction() const ASMJIT_NOTHROW { return _currentFunction; }
//! @brief Create function prolog (function begin section).
//!
//! Function prologs and epilogs are standardized sequences of instructions
//! thats used to build functions. If you are using @c Function and
//! @c AsmJit::Compiler::newFunction() to make a function, keep in mind that
//! it creates prolog (by @c newFunction()) and epilog (by @c endFunction())
//! for you.
//!
//! @note Never use prolog after @c newFunction() method. It will create
//! prolog for you!
//!
//! @note Compiler can optimize prologs and epilogs.
//!
//! @sa @c Prolog, @c Function.
Prolog* newProlog(Function* f) ASMJIT_NOTHROW;
//! @brief Create function epilog (function leave section).
//!
//! Function prologs and epilogs are standardized sequences of instructions
//! thats used to build functions. If you are using @c Function and
//! @c AsmJit::Compiler::newFunction() to make a function, keep in mind that
//! it creates prolog (by @c newFunction()) and epilog (by @c endFunction())
//! for you.
//!
//! @note Never use epilog before @c endFunction() method. It will create
//! epilog for you!
//!
//! @note Compiler can optimize prologs and epilogs.
//!
//! @sa @c Epilog, @c Function.
Epilog* newEpilog(Function* f) ASMJIT_NOTHROW;
// --------------------------------------------------------------------------
// [Registers allocator / Variables]
// --------------------------------------------------------------------------
//! @brief Convenience method that calls:
//! Compiler::currentFunction()->argument()
//! @sa @c Function::argument()
Variable* argument(SysInt i) ASMJIT_NOTHROW;
//! @brief Convenience method that calls:
//! Compiler::currentFunction()->newVariable()
//! @sa @c Function::newVariable()
Variable* newVariable(UInt8 type, UInt8 priority = 10, UInt8 preferredRegister = NO_REG) ASMJIT_NOTHROW;
//! @brief Convenience method that calls:
//! Compiler::currentFunction()->alloc()
//! @sa @c Function::alloc()
bool alloc(Variable* v,
UInt8 mode = VARIABLE_ALLOC_READWRITE,
UInt8 preferredRegister = NO_REG) ASMJIT_NOTHROW;
//! @brief Convenience method that calls:
//! Compiler::currentFunction()->spill()
//! @sa @c Function::spill()
bool spill(Variable* v) ASMJIT_NOTHROW;
//! @brief Convenience method that calls:
//! Compiler::currentFunction()->unuse()
//! @sa @c Function::unuse()
void unuse(Variable* v) ASMJIT_NOTHROW;
//! @brief Convenience method that calls:
//! Compiler::currentFunction()->spillAll()
//! @sa @c Function::spillAll()
void spillAll() ASMJIT_NOTHROW;
//! @brief Convenience method that calls:
//! Compiler::currentFunction()->spillAllGp()
//! @sa @c Function::spillAllGp()
void spillAllGp() ASMJIT_NOTHROW;
//! @brief Convenience method that calls:
//! Compiler::currentFunction()->spillAllMm()
//! @sa @c Function::spillAllMm()
void spillAllMm() ASMJIT_NOTHROW;
//! @brief Convenience method that calls:
//! Compiler::currentFunction()->spillAllXmm()
//! @sa @c Function::spillAllXmm()
void spillAllXmm() ASMJIT_NOTHROW;
//! @brief Convenience method that calls:
//! Compiler::currentFunction()->spillRegister()
//! @sa @c Function::spillRegister()
void spillRegister(const BaseReg& reg) ASMJIT_NOTHROW;
SysInt numFreeGp() const ASMJIT_NOTHROW;
SysInt numFreeMm() const ASMJIT_NOTHROW;
SysInt numFreeXmm() const ASMJIT_NOTHROW;
//! @brief Convenience method that calls:
//! Compiler::currentFunction()->isPrevented()
//! @sa @c Function::isPrevented()
bool isPrevented(Variable* v) ASMJIT_NOTHROW;
//! @brief Convenience method that calls:
//! Compiler::currentFunction()->addPrevented()
//! @sa @c Function::addPrevented()
void addPrevented(Variable* v) ASMJIT_NOTHROW;
//! @brief Convenience method that calls:
//! Compiler::currentFunction()->removePrevented()
//! @sa @c Function::removePrevented()
void removePrevented(Variable* v) ASMJIT_NOTHROW;
//! @brief Convenience method that calls:
//! Compiler::currentFunction()->clearPrevented()
//! @sa @c Function::clearPrevented()
void clearPrevented() ASMJIT_NOTHROW;
// --------------------------------------------------------------------------
// [State]
// --------------------------------------------------------------------------
//! @brief Convenience method that calls:
//! Compiler::currentFunction()->saveState()
//! @sa @c Function::saveState().
State* saveState() ASMJIT_NOTHROW;
//! @brief Convenience method that calls:
//! Compiler::currentFuncion()->restoreState()
//! @sa @c Function::restoreState().
void restoreState(State* state) ASMJIT_NOTHROW;
//! @brief Convenience method that calls:
//! Compiler::currentFuncion()->setState()
//! @sa @c Function::setState()
void setState(State* state) ASMJIT_NOTHROW;
// -------------------------------------------------------------------------
// [Labels]
// -------------------------------------------------------------------------
//! @brief Create and return new @a Label managed by compiler.
//!
//! Labels created by compiler are same objects as labels created for
//! @c Assembler. There is only one limitation that if you are using
//! @c Compiler each label must be created by @c AsmJit::Compiler::newLabel()
//! method.
Label* newLabel() ASMJIT_NOTHROW;
// -------------------------------------------------------------------------
// [Jump Table]
// -------------------------------------------------------------------------
JumpTable* newJumpTable() ASMJIT_NOTHROW;
// -------------------------------------------------------------------------
// [Memory Management]
// -------------------------------------------------------------------------
//! @brief Create object managed by compiler internal memory manager.
template<typename T>
inline T* newObject() ASMJIT_NOTHROW
{
void* addr = _zoneAlloc(sizeof(T));
return new(addr) T(reinterpret_cast<Compiler*>(this));
}
//! @brief Create object managed by compiler internal memory manager.
template<typename T, typename P1>
inline T* newObject(P1 p1) ASMJIT_NOTHROW
{
void* addr = _zoneAlloc(sizeof(T));
return new(addr) T(reinterpret_cast<Compiler*>(this), p1);
}
//! @brief Create object managed by compiler internal memory manager.
template<typename T, typename P1, typename P2>
inline T* newObject(P1 p1, P2 p2) ASMJIT_NOTHROW
{
void* addr = _zoneAlloc(sizeof(T));
return new(addr) T(reinterpret_cast<Compiler*>(this), p1, p2);
}
//! @brief Create object managed by compiler internal memory manager.
template<typename T, typename P1, typename P2, typename P3>
inline T* newObject(P1 p1, P2 p2, P3 p3) ASMJIT_NOTHROW
{
void* addr = _zoneAlloc(sizeof(T));
return new(addr) T(reinterpret_cast<Compiler*>(this), p1, p2, p3);
}
//! @brief Create object managed by compiler internal memory manager.
template<typename T, typename P1, typename P2, typename P3, typename P4>
inline T* newObject(P1 p1, P2 p2, P3 p3, P4 p4) ASMJIT_NOTHROW
{
void* addr = _zoneAlloc(sizeof(T));
return new(addr) T(reinterpret_cast<Compiler*>(this), p1, p2, p3, p4);
}
//! @brief Create object managed by compiler internal memory manager.
template<typename T, typename P1, typename P2, typename P3, typename P4, typename P5>
inline T* newObject(P1 p1, P2 p2, P3 p3, P4 p4, P5 p5) ASMJIT_NOTHROW
{
void* addr = _zoneAlloc(sizeof(T));
return new(addr) T(reinterpret_cast<Compiler*>(this), p1, p2, p3, p4, p5);
}
//! @brief Internal function that registers operand @a op in compiler.
//!
//! Operand registration means adding @a op to internal operands list and
//! setting operand id.
//!
//! @note Operand @a op should by allocated by @c Compiler or you must
//! guarantee that it will be not destroyed before @c Compiler is destroyed.
void _registerOperand(Operand* op) ASMJIT_NOTHROW;
// -------------------------------------------------------------------------
// [Jumps / Calls]
// -------------------------------------------------------------------------
using Serializer::jmp;
using Serializer::call;
void jumpToTable(JumpTable* jt, const Register& index) ASMJIT_NOTHROW;
SysInt _addTarget(void* target) ASMJIT_NOTHROW;
void _jmpAndRestore(UInt32 code, Label* label, State* state) ASMJIT_NOTHROW;
// -------------------------------------------------------------------------
// [Intrinsics]
// -------------------------------------------------------------------------
//! @brief Intrinsics helper method.
//! @internal
void op_var32(UInt32 code, const Int32Ref& a) ASMJIT_NOTHROW;
//! @brief Intrinsics helper method.
//! @internal
void op_reg32_var32(UInt32 code, const Register& a, const Int32Ref& b) ASMJIT_NOTHROW;
//! @brief Intrinsics helper method.
//! @internal
void op_var32_reg32(UInt32 code, const Int32Ref& a, const Register& b) ASMJIT_NOTHROW;
//! @brief Intrinsics helper method.
//! @internal
void op_var32_imm(UInt32 code, const Int32Ref& a, const Immediate& b) ASMJIT_NOTHROW;
#if defined(ASMJIT_X64)
//! @brief Intrinsics helper method.
//! @internal
void op_var64(UInt32 code, const Int64Ref& a) ASMJIT_NOTHROW;
//! @brief Intrinsics helper method.
//! @internal
void op_reg64_var64(UInt32 code, const Register& a, const Int64Ref& b) ASMJIT_NOTHROW;
//! @brief Intrinsics helper method.
//! @internal
void op_var64_reg64(UInt32 code, const Int64Ref& a, const Register& b) ASMJIT_NOTHROW;
//! @brief Intrinsics helper method.
//! @internal
void op_var64_imm(UInt32 code, const Int64Ref& a, const Immediate& b) ASMJIT_NOTHROW;
#endif // ASMJIT_X64
// -------------------------------------------------------------------------
// [EmitX86]
// -------------------------------------------------------------------------
virtual void _inlineComment(const char* text, SysInt len = -1) ASMJIT_NOTHROW;
virtual void _emitX86(UInt32 code, const Operand* o1, const Operand* o2, const Operand* o3) ASMJIT_NOTHROW;
// -------------------------------------------------------------------------
// [Embed]
// -------------------------------------------------------------------------
virtual void _embed(const void* dataPtr, SysUInt dataSize) ASMJIT_NOTHROW;
// -------------------------------------------------------------------------
// [Align]
// -------------------------------------------------------------------------
virtual void align(SysInt m) ASMJIT_NOTHROW;
// -------------------------------------------------------------------------
// [Bind]
// -------------------------------------------------------------------------
virtual void bind(Label* label) ASMJIT_NOTHROW;
// -------------------------------------------------------------------------
// [Make]
// -------------------------------------------------------------------------
virtual void* make(MemoryManager* memoryManager = NULL, UInt32 allocType = MEMORY_ALLOC_FREEABLE) ASMJIT_NOTHROW;
//! @brief Method that will emit everything to @c Assembler instance @a a.
void serialize(Assembler& a) ASMJIT_NOTHROW;
// -------------------------------------------------------------------------
// [Variables]
// -------------------------------------------------------------------------
private:
//! @brief First emittable.
Emittable* _first;
//! @brief Last emittable.
Emittable* _last;
//! @brief Current emittable.
Emittable* _current;
//! @brief Operands list (operand id is index in this list, id 0 is not valid).
OperandList _operands;
//! @brief Current function.
Function* _currentFunction;
//! @brief Label id counter (starts from 1).
UInt32 _labelIdCounter;
//! @brief Jump table label.
Label* _jumpTableLabel;
//! @brief Jump table entities.
PodVector<void*> _jumpTableData;
//! @brief Buffer for inline comment (for next instruction).
const char* _inlineCommentBuffer;
friend struct Instruction;
friend struct Variable;
};
// ============================================================================
// [AsmJit::CompilerIntrinsics]
// ============================================================================
//! @brief Implementation of @c Compiler intrinsics.
//!
//! Methods in this class are implemented here, because we wan't to hide them
//! in shared libraries. These methods should be never exported by C++ compiler.
//!
//! @sa @c AsmJit::Compiler.
struct ASMJIT_HIDDEN CompilerIntrinsics : public CompilerCore
{
//! @brief Create @c CompilerIntrinsics instance. Always use @c AsmJit::Compiler.
inline CompilerIntrinsics() ASMJIT_NOTHROW {}
// --------------------------------------------------------------------------
// [jmpAndRestore]
// --------------------------------------------------------------------------
inline void jAndRestore(CONDITION cc, Label* label, State* state)
{
ASMJIT_ASSERT(static_cast<UInt32>(cc) <= 0xF);
_jmpAndRestore(_jcctable[cc], label, state);
}
inline void jaAndRestore (Label* label, State* state) { _jmpAndRestore(INST_JA , label, state); }
inline void jaeAndRestore (Label* label, State* state) { _jmpAndRestore(INST_JAE , label, state); }
inline void jbAndRestore (Label* label, State* state) { _jmpAndRestore(INST_JB , label, state); }
inline void jbeAndRestore (Label* label, State* state) { _jmpAndRestore(INST_JBE , label, state); }
inline void jcAndRestore (Label* label, State* state) { _jmpAndRestore(INST_JC , label, state); }
inline void jeAndRestore (Label* label, State* state) { _jmpAndRestore(INST_JE , label, state); }
inline void jgAndRestore (Label* label, State* state) { _jmpAndRestore(INST_JG , label, state); }
inline void jgeAndRestore (Label* label, State* state) { _jmpAndRestore(INST_JGE , label, state); }
inline void jlAndRestore (Label* label, State* state) { _jmpAndRestore(INST_JL , label, state); }
inline void jleAndRestore (Label* label, State* state) { _jmpAndRestore(INST_JLE , label, state); }
inline void jnaAndRestore (Label* label, State* state) { _jmpAndRestore(INST_JNA , label, state); }
inline void jnaeAndRestore(Label* label, State* state) { _jmpAndRestore(INST_JNAE, label, state); }
inline void jnbAndRestore (Label* label, State* state) { _jmpAndRestore(INST_JNB , label, state); }
inline void jnbeAndRestore(Label* label, State* state) { _jmpAndRestore(INST_JNBE, label, state); }
inline void jncAndRestore (Label* label, State* state) { _jmpAndRestore(INST_JNC , label, state); }
inline void jneAndRestore (Label* label, State* state) { _jmpAndRestore(INST_JNE , label, state); }
inline void jngAndRestore (Label* label, State* state) { _jmpAndRestore(INST_JNG , label, state); }
inline void jngeAndRestore(Label* label, State* state) { _jmpAndRestore(INST_JNGE, label, state); }
inline void jnlAndRestore (Label* label, State* state) { _jmpAndRestore(INST_JNL , label, state); }
inline void jnleAndRestore(Label* label, State* state) { _jmpAndRestore(INST_JNLE, label, state); }
inline void jnoAndRestore (Label* label, State* state) { _jmpAndRestore(INST_JNO , label, state); }
inline void jnpAndRestore (Label* label, State* state) { _jmpAndRestore(INST_JNP , label, state); }
inline void jnsAndRestore (Label* label, State* state) { _jmpAndRestore(INST_JNS , label, state); }
inline void jnzAndRestore (Label* label, State* state) { _jmpAndRestore(INST_JNZ , label, state); }
inline void joAndRestore (Label* label, State* state) { _jmpAndRestore(INST_JO , label, state); }
inline void jpAndRestore (Label* label, State* state) { _jmpAndRestore(INST_JP , label, state); }
inline void jpeAndRestore (Label* label, State* state) { _jmpAndRestore(INST_JPE , label, state); }
inline void jpoAndRestore (Label* label, State* state) { _jmpAndRestore(INST_JPO , label, state); }
inline void jsAndRestore (Label* label, State* state) { _jmpAndRestore(INST_JS , label, state); }
inline void jzAndRestore (Label* label, State* state) { _jmpAndRestore(INST_JZ , label, state); }
inline void jmpAndRestore (Label* label, State* state) { _jmpAndRestore(INST_JMP , label, state); }
// --------------------------------------------------------------------------
// [Intrinsics]
// --------------------------------------------------------------------------
using SerializerIntrinsics::adc;
using SerializerIntrinsics::add;
using SerializerIntrinsics::and_;
using SerializerIntrinsics::cmp;
using SerializerIntrinsics::dec;
using SerializerIntrinsics::inc;
using SerializerIntrinsics::mov;
using SerializerIntrinsics::neg;
using SerializerIntrinsics::not_;
using SerializerIntrinsics::or_;
using SerializerIntrinsics::sbb;
using SerializerIntrinsics::sub;
using SerializerIntrinsics::xor_;
inline void adc(const Register& dst, const Int32Ref& src) { op_reg32_var32(INST_ADC, dst, src); }
inline void adc(const Int32Ref& dst, const Register& src) { op_var32_reg32(INST_ADC, dst, src); }
inline void adc(const Int32Ref& dst, const Immediate& src) { op_var32_imm(INST_ADC, dst, src); }
inline void add(const Register& dst, const Int32Ref& src) { op_reg32_var32(INST_ADD, dst, src); }
inline void add(const Int32Ref& dst, const Register& src) { op_var32_reg32(INST_ADD, dst, src); }
inline void add(const Int32Ref& dst, const Immediate& src) { op_var32_imm(INST_ADD, dst, src); }
inline void and_(const Register& dst, const Int32Ref& src) { op_reg32_var32(INST_AND, dst, src); }
inline void and_(const Int32Ref& dst, const Register& src) { op_var32_reg32(INST_AND, dst, src); }
inline void and_(const Int32Ref& dst, const Immediate& src) { op_var32_imm(INST_AND, dst, src); }
inline void cmp(const Register& dst, const Int32Ref& src) { op_reg32_var32(INST_CMP, dst, src); }
inline void cmp(const Int32Ref& dst, const Register& src) { op_var32_reg32(INST_CMP, dst, src); }
inline void cmp(const Int32Ref& dst, const Immediate& src) { op_var32_imm(INST_CMP, dst, src); }
inline void dec(const Int32Ref& dst) { op_var32(INST_DEC, dst); }
inline void inc(const Int32Ref& dst) { op_var32(INST_INC, dst); }
inline void neg(const Int32Ref& dst) { op_var32(INST_NEG, dst); }
inline void not_(const Int32Ref& dst) { op_var32(INST_NOT, dst); }
inline void mov(const Register& dst, const Int32Ref& src) { op_reg32_var32(INST_MOV, dst, src); }
inline void mov(const Int32Ref& dst, const Register& src) { op_var32_reg32(INST_MOV, dst, src); }
inline void mov(const Int32Ref& dst, const Immediate& src) { op_var32_imm(INST_MOV, dst, src); }
inline void or_(const Register& dst, const Int32Ref& src) { op_reg32_var32(INST_OR, dst, src); }
inline void or_(const Int32Ref& dst, const Register& src) { op_var32_reg32(INST_OR, dst, src); }
inline void or_(const Int32Ref& dst, const Immediate& src) { op_var32_imm(INST_OR, dst, src); }
inline void sbb(const Register& dst, const Int32Ref& src) { op_reg32_var32(INST_SBB, dst, src); }
inline void sbb(const Int32Ref& dst, const Register& src) { op_var32_reg32(INST_SBB, dst, src); }
inline void sbb(const Int32Ref& dst, const Immediate& src) { op_var32_imm(INST_SBB, dst, src); }
inline void sub(const Register& dst, const Int32Ref& src) { op_reg32_var32(INST_SUB, dst, src); }
inline void sub(const Int32Ref& dst, const Register& src) { op_var32_reg32(INST_SUB, dst, src); }
inline void sub(const Int32Ref& dst, const Immediate& src) { op_var32_imm(INST_SUB, dst, src); }
inline void xor_(const Register& dst, const Int32Ref& src) { op_reg32_var32(INST_XOR, dst, src); }
inline void xor_(const Int32Ref& dst, const Register& src) { op_var32_reg32(INST_XOR, dst, src); }
inline void xor_(const Int32Ref& dst, const Immediate& src) { op_var32_imm(INST_XOR, dst, src); }
#if defined(ASMJIT_X64)
inline void adc(const Register& dst, const Int64Ref& src) { op_reg64_var64(INST_ADC, dst, src); }
inline void adc(const Int64Ref& dst, const Register& src) { op_var64_reg64(INST_ADC, dst, src); }
inline void adc(const Int64Ref& dst, const Immediate& src) { op_var64_imm(INST_ADC, dst, src); }
inline void add(const Register& dst, const Int64Ref& src) { op_reg64_var64(INST_ADD, dst, src); }
inline void add(const Int64Ref& dst, const Register& src) { op_var64_reg64(INST_ADD, dst, src); }
inline void add(const Int64Ref& dst, const Immediate& src) { op_var64_imm(INST_ADD, dst, src); }
inline void and_(const Register& dst, const Int64Ref& src) { op_reg64_var64(INST_AND, dst, src); }
inline void and_(const Int64Ref& dst, const Register& src) { op_var64_reg64(INST_AND, dst, src); }
inline void and_(const Int64Ref& dst, const Immediate& src) { op_var64_imm(INST_AND, dst, src); }
inline void cmp(const Register& dst, const Int64Ref& src) { op_reg64_var64(INST_CMP, dst, src); }
inline void cmp(const Int64Ref& dst, const Register& src) { op_var64_reg64(INST_CMP, dst, src); }
inline void cmp(const Int64Ref& dst, const Immediate& src) { op_var64_imm(INST_CMP, dst, src); }
inline void dec(const Int64Ref& dst) { op_var64(INST_DEC, dst); }
inline void inc(const Int64Ref& dst) { op_var64(INST_INC, dst); }
inline void neg(const Int64Ref& dst) { op_var64(INST_NEG, dst); }
inline void not_(const Int64Ref& dst) { op_var64(INST_NOT, dst); }
inline void mov(const Register& dst, const Int64Ref& src) { op_reg64_var64(INST_MOV, dst, src); }
inline void mov(const Int64Ref& dst, const Register& src) { op_var64_reg64(INST_MOV, dst, src); }
inline void mov(const Int64Ref& dst, const Immediate& src) { op_var64_imm(INST_MOV, dst, src); }
inline void or_(const Register& dst, const Int64Ref& src) { op_reg64_var64(INST_OR, dst, src); }
inline void or_(const Int64Ref& dst, const Register& src) { op_var64_reg64(INST_OR, dst, src); }
inline void or_(const Int64Ref& dst, const Immediate& src) { op_var64_imm(INST_OR, dst, src); }
inline void sbb(const Register& dst, const Int64Ref& src) { op_reg64_var64(INST_SBB, dst, src); }
inline void sbb(const Int64Ref& dst, const Register& src) { op_var64_reg64(INST_SBB, dst, src); }
inline void sbb(const Int64Ref& dst, const Immediate& src) { op_var64_imm(INST_SBB, dst, src); }
inline void sub(const Register& dst, const Int64Ref& src) { op_reg64_var64(INST_SUB, dst, src); }
inline void sub(const Int64Ref& dst, const Register& src) { op_var64_reg64(INST_SUB, dst, src); }
inline void sub(const Int64Ref& dst, const Immediate& src) { op_var64_imm(INST_SUB, dst, src); }
inline void xor_(const Register& dst, const Int64Ref& src) { op_reg64_var64(INST_XOR, dst, src); }
inline void xor_(const Int64Ref& dst, const Register& src) { op_var64_reg64(INST_XOR, dst, src); }
inline void xor_(const Int64Ref& dst, const Immediate& src) { op_var64_imm(INST_XOR, dst, src); }
#endif // ASMJIT_X64
};
// ============================================================================
// [AsmJit::Compiler]
// ============================================================================
//! @brief Compiler - high level code generation.
//!
//! This class is used to store instruction stream and allows to modify
//! it on the fly. It uses different concept than @c AsmJit::Assembler class
//! and in fact @c AsmJit::Assembler is only used as a backend. Compiler never
//! emits machine code and each instruction you use is stored to instruction
//! array instead. This allows to modify instruction stream later and for
//! example to reorder instructions to make better performance.
//!
//! Using @c AsmJit::Compiler moves code generation to higher level. Higher
//! level constructs allows to write more abstract and extensible code that
//! is not possible with pure @c AsmJit::Assembler class. Because
//! @c AsmJit::Compiler needs to create many objects and lifetime of these
//! objects is small (same as @c AsmJit::Compiler lifetime itself) it uses
//! very fast memory management model. This model allows to create object
//! instances in nearly zero time (compared to @c malloc() or @c new()
//! operators) so overhead by creating machine code by @c AsmJit::Compiler
//! is minimized.
//!
//! <b>Code Generation</b>
//!
//! First that is needed to know about compiler is that compiler never emits
//! machine code. It's used as a middleware between @c AsmJit::Assembler and
//! your code. There is also convenience method @c make() that allows to
//! generate machine code directly without creating @c AsmJit::Assembler
//! instance.
//!
//! Example how to generate machine code using @c Assembler and @c Compiler:
//!
//! @code
//! // Assembler instance is low level code generation class that emits
//! // machine code.
//! Assembler a;
//!
//! // Compiler instance is high level code generation class that stores all
//! // instructions in internal representation.
//! Compiler c;
//!
//! // ... put your code using Compiler instance ...
//!
//! // Final step - generate code. AsmJit::Compiler::serialize() will serialize
//! // all instructions into Assembler and this ensures generating real machine
//! // code.
//! c.serialize(a);
//!
//! // Your function
//! void* fn = a.make();
//! @endcode
//!
//! Example how to generate machine code using only @c Compiler (preferred):
//!
//! @code
//! // Compiler instance is enough.
//! Compiler c;
//!
//! // ... put your code using Compiler instance ...
//!
//! // Your function
//! void* fn = c.make();
//! @endcode
//!
//! You can see that there is @c AsmJit::Compiler::serialize() function that
//! emits instructions into @c AsmJit::Assembler(). This layered architecture
//! means that each class is used for something different and there is no code
//! duplication. For convenience there is also @c AsmJit::Compiler::make()
//! method that can create your function using @c AsmJit::Assembler, but
//! internally (this is preffered bahavior when using @c AsmJit::Compiler).
//!
//! @c make() allocates memory using global memory manager instance, if your
//! function lifetime is over, you should free that memory by
//! @c AsmJit::MemoryManager::free() method.
//!
//! @code
//! // Compiler instance is enough.
//! Compiler c;
//!
//! // ... put your code using Compiler instance ...
//!
//! // Your function
//! void* fn = c.make();
//!
//! // Free it if you don't want it anymore
//! // (using global memory manager instance)
//! MemoryManager::global()->free(fn);
//! @endcode
//!
//! <b>Functions</b>
//!
//! To build functions with @c Compiler, see @c AsmJit::Compiler::newFunction()
//! method.
//!
//! <b>Variables</b>
//!
//! Compiler also manages your variables and function arguments. Using manual
//! register allocation is not recommended way and it must be done carefully.
//! See @c AsmJit::VariableRef and related classes how to work with variables
//! and next example how to use AsmJit API to create function and manage them:
//!
//! @code
//! // Compiler and function declaration - void f(int*);
//! Compiler c;
//! Function& f = *c.newFunction(CALL_CONV_DEFAULT, BuildFunction1<int*>());
//!
//! // Get argument variable (it's pointer)
//! PtrRef a1(f.argument(0));
//!
//! // Create your variables
//! Int32Ref x1(f.newVariable(VARIABLE_TYPE_INT32));
//! Int32Ref x2(f.newVariable(VARIABLE_TYPE_INT32));
//!
//! // Init your variables
//! c.mov(x1.r(), 1);
//! c.mov(x2.r(), 2);
//!
//! // ... your code ...
//! c.add(x1.r(), x2.r());
//! // ... your code ...
//!
//! // Store result to a given pointer in first argument
//! c.mov(dword_ptr(a1.c()), x1.c());
//!
//! // Make function
//! typedef void (*MyFn)(int*);
//! MyFn fn = function_cast<MyFn>(c.make());
//! @endcode
//!
//! There was presented small code snippet with variables, but it's needed to
//! explain it more. You can see that there are more variable types that can
//! be used. Most useful variables that can be allocated to general purpose
//! registers are variables wrapped to @c Int32Ref, @c Int64Ref, @c SysIntRef
//! and @c PtrRef. Only @c Int64Ref is limited to 64 bit architecture.
//! @c SysIntRef and @c PtrRef variables are equal and it's size depends to
//! architecture (32 or 64 bits).
//!
//! Compiler is not using variables directly, instead you need to create the
//! function and create variables through @c AsmJit::Function. In code you will
//! always work with @c AsmJit::Compiler and @c AsmJit::Function together.
//!
//! Each variable contains state that describes where it is currently allocated
//! and if it's used. Life of variables is based on reference counting and if
//! variable is dereferenced to zero its life ends.
//!
//! Variable states:
//!
//! - Unused (@c AsmJit::VARIABLE_STATE_UNUSED) - State that is assigned to
//! newly created variables or to not used variables (dereferenced to zero).
//! - In register (@c AsmJit::VARIABLE_STATE_REGISTER) - State that means that
//! variable is currently allocated in register.
//! - In memory (@c AsmJit::VARIABLE_STATE_MEMORY) - State that means that
//! variable is currently only in memory location.
//!
//! When you create new variable, its state is always @c VARIABLE_STATE_UNUSED,
//! allocating it to register or spilling to memory changes this state to
//! @c VARIABLE_STATE_REGISTER or @c VARIABLE_STATE_MEMORY, respectively.
//! During variable lifetime it's usual that its state is changed multiple
//! times. To generate better code, you can control allocating and spilling
//! by using up to four types of methods that allows it (see next list).
//!
//! Explicit variable allocating / spilling methods:
//!
//! - @c VariableRef::alloc() - Explicit method to alloc variable into
//! register. You can use this before loops or code blocks.
//!
//! - @c VariableRef::spill() - Explicit method to spill variable. If variable
//! is in register and you call this method, it's moved to its home memory
//! location. If variable is not in register no operation is performed.
//!
//! Implicit variable allocating / spilling methods:
//!
//! - @c VariableRef::r() - Method used to allocate (if it's not previously
//! allocated) variable to register for read / write. In most cases
//! this is the right method to use in your code. If variable is in
//! memory and you use this method it's allocated and moved to register.
//! If variable is already in register or it's marked as unused this
//! method does nothing.
//!
//! - @c VariableRef::x() - Method used to allocate variable for write only.
//! In AsmJit this means completely overwrite it without using it's value.
//! This method is helpful when you want to prevent from copying variable
//! from memory to register to save one mov() instruction. If you want
//! to clear or set your variable to something it's recommended to use
//! @c VariableRef::x().
//!
//! - @c VariableRef::c() - Method used to use variable as a constant.
//! Constants means that you will not change that variable or you don't
//! want to mark variable as changed. If variable is not marked as changed
//! and spill happens you will save one mov() instruction that is needed
//! to copy variable from register to its home address.
//!
//! - @c VariableRef::m() - Method used to access variable memory address. If
//! variable is allocated in register and you call this method, it's
//! spilled, in all other cases it does nothing.
//!
//! Next example shows how allocating and spilling works:
//!
//! @code
//! // Small example to show how variable allocating and spilling works
//!
//! // Your compiler
//! Compiler c;
//!
//! // Your variable
//! Int32Ref var = ...;
//!
//! // Make sure variable is spilled
//! var.spill();
//!
//! // 1. Example: using var.r()
//! c.mov(var.r(), imm(0));
//! var.spill();
//! // Generated code:
//! // mov var.reg, [var.home]
//! // mov var.reg, 0
//! // mov [var.home], var.reg
//!
//! // 2. Example: using var.x()
//! c.mov(var.x(), imm(0));
//! var.spill();
//! // Generated code:
//! // --- no alloc, .x() inhibits it.
//! // mov var.reg, 0
//! // mov [var.home], var.reg
//!
//! // 3. Example: using var.c()
//! c.mov(var.c(), imm(0));
//! var.spill();
//! // Generated code:
//! // mov var.reg, [var.home]
//! // mov var.reg, 0
//! // --- no spill, .c() means that you are not changing it, it's 'c'onstant
//!
//! // 4. Example: using var.m()
//! c.mov(var.m(), imm(0));
//! var.spill();
//! // Generated code:
//! // --- no alloc, because we are not allocating it
//! // mov [var.home], 0
//! // --- no spill, because variable is not allocated
//!
//! // 5. Example: using var.x(), setChanged()
//! c.mov(var.x(),imm(0));
//! var.setChanged(false);
//! var.spill();
//! // Generated code:
//! // --- no alloc, .x() inhibits it.
//! // mov var.reg, 0
//! // --- no spill, setChanged(false) marked variable as unmodified
//! @endcode
//!
//! Please see AsmJit tutorials (testcompiler.cpp and testvariables.cpp) for
//! more complete examples.
//!
//! <b>Intrinsics Extensions</b>
//!
//! Compiler supports extensions to intrinsics implemented in
//! @c AsmJit::Serializer that enables to use variables in instructions without
//! specifying to use it as register or as memory operand. Sometimes is better
//! not to alloc variable for each read or write. There is limitation that you
//! can use variable without specifying if it's in register or in memory
//! location only for one operand. This is because x86/x64 architecture not
//! allows to use two memory operands in one instruction and this could
//! happen without this restriction (two variables in memory).
//!
//! @code
//! // Small example to show how intrinsics extensions works
//!
//! // Your compiler
//! Compiler c;
//!
//! // Your variable
//! Int32Ref var = ...;
//!
//! // Make sure variable is spilled
//! var.spill();
//!
//! // 1. Example: Allocated variable
//! var.alloc()
//! c.mov(var, imm(0));
//! var.spill();
//! // Generated code:
//! // mov var.reg, [var.home]
//! // mov var.reg, 0
//! // mov [var.home], var.reg
//!
//! // 2. Example: Memory variable
//! c.mov(var, imm(0));
//! var.spill();
//! // Generated code:
//! // --- no alloc, we want variable in memory
//! // mov [var.home], 0
//! // --- no spill, becuase variable in not allocated
//! @endcode
//!
//! <b>Memory Management</b>
//!
//! @c Compiler Memory management follows these rules:
//! - Everything created by @c Compiler is always freed by @c Compiler.
//! - To get decent performance, compiler always uses larger buffer for
//! objects to allocate and when compiler instance is destroyed, this
//! buffer is freed. Destructors of active objects are called when
//! destroying compiler instance. Destructors of abadonded compiler
//! objects are called immediately after abadonding it.
//!
//! This means that you can't use any @c Compiler object after destructing it,
//! it also means that each object like @c Label, @c Variable nad others are
//! created and managed by @c Compiler itself.
//!
//! <b>Compiling process details</b>
//!
//! This section is here for people interested in the compiling process. There
//! are few steps that must be done for each compiled function (or your code).
//!
//! When your Compiler instance is ready, you can create function and add
//! emittables using intrinsics or higher level methods implemented in the
//! @c AsmJit::Compiler. When you are done serializing instructions you will
//! usually call @c AsmJit::Compiler::make() method to serialize all emittables
//! to @c AsmJit::Assembler. Next steps shows what's done internally before code
//! is serialized into @c AsmJit::Assembler
//! (implemented in @c AsmJit::Compiler::serialize() method).
//!
//! 1. All emittables are traversed (from first to last) and method
//! @c AsmJit::Emittable::prepare() is called. This signalizes to all
//! emittables that instruction generation step is over and now they
//! should prepare to code generation. In this step can be processed
//! variables, states, etc...
//! 2. All emittables are traversed (from first to last) and method
//! @c AsmJit::Emittable::emit() is called. In this step each emittable
//! can serialize real assembler instructions into @c AsmJit::Assembler
//! instance. This step also generates function prolog and epilog.
//! 3. All emittables are traversed (from first to last) and method
//! @c AsmJit::Emittable::postEmit() is called. Post emitting is used
//! to embed data after function body (not only user data, but also some
//! helper data that can help generating jumps, variables restore / save
//! sequences, condition blocks).
//! 4. Jump tables data are emitted.
//!
//! When everything here ends, @c AsmJit::Assembler contains binary stream
//! that needs only relocation to be callable.
//!
//! <b>Differences summary to @c AsmJit::Assembler</b>
//!
//! - Instructions are not translated to machine code immediately, they are
//! stored as @c Emmitable's (see @c AsmJit::Instruction).
//! - Each @c Label must be allocated by @c AsmJit::Compiler::newLabel().
//! - Contains function builder.
//! - Contains register allocator / variables management.
//! - Contains a lot of helper methods to simplify code generation.
struct ASMJIT_API Compiler : public CompilerIntrinsics
{
//! @brief Create a new @c Compiler instance.
Compiler() ASMJIT_NOTHROW;
//! @brief Destroy @c Compiler instance.
virtual ~Compiler() ASMJIT_NOTHROW;
};
//! @}
} // AsmJit namespace
// [Warnings-Pop]
#include "WarningsPop.h"
// [Guard]
#endif // _ASMJIT_COMPILERX86X64_H