2023-12-22 11:57:49 +00:00
// SPDX-FileCopyrightText: 2002-2023 PCSX2 Dev Team
// SPDX-License-Identifier: LGPL-3.0+
2009-03-01 20:44:48 +00:00
# pragma once
2021-09-01 20:31:46 +00:00
# include "common/Threading.h"
2022-05-09 10:28:57 +00:00
# include "common/Assertions.h"
# include "common/Pcsx2Defs.h"
2021-09-01 20:31:46 +00:00
2016-01-16 18:31:58 +00:00
static const uint iREGCNT_XMM = 16 ;
static const uint iREGCNT_GPR = 16 ;
2009-04-14 12:37:48 +00:00
2021-09-06 18:28:26 +00:00
enum XMMSSEType
{
XMMT_INT = 0 , // integer (sse2 only)
XMMT_FPS = 1 , // floating point
//XMMT_FPD = 3, // double
2009-04-14 12:37:48 +00:00
} ;
2022-05-09 10:37:53 +00:00
extern thread_local u8 * x86Ptr ;
extern thread_local XMMSSEType g_xmmtypes [ iREGCNT_XMM ] ;
2009-04-14 01:26:57 +00:00
2009-04-24 11:25:10 +00:00
namespace x86Emitter
{
2022-12-25 12:14:15 +00:00
// Win32 requires 32 bytes of shadow stack in the caller's frame.
# ifdef _WIN32
static constexpr int SHADOW_STACK_SIZE = 32 ;
# else
static constexpr int SHADOW_STACK_SIZE = 0 ;
# endif
2009-04-24 11:25:10 +00:00
2021-09-06 18:28:26 +00:00
extern void xWrite8 ( u8 val ) ;
extern void xWrite16 ( u16 val ) ;
extern void xWrite32 ( u32 val ) ;
extern void xWrite64 ( u64 val ) ;
2009-04-24 11:25:10 +00:00
2021-09-06 18:28:26 +00:00
extern const char * xGetRegName ( int regid , int operandSize ) ;
2009-11-06 21:45:30 +00:00
2021-09-06 18:28:26 +00:00
//------------------------------------------------------------------
// templated version of is_s8 is required, so that u16's get correct sign extension treatment.
template < typename T >
static __fi bool is_s8 ( T imm )
{
2022-04-11 10:33:18 +00:00
return ( s8 ) imm = = ( typename std : : make_signed < T > : : type ) imm ;
2021-09-06 18:28:26 +00:00
}
2009-04-07 08:42:25 +00:00
2021-09-06 18:28:26 +00:00
template < typename T >
void xWrite ( T val ) ;
2009-04-07 08:42:25 +00:00
2009-11-06 21:45:30 +00:00
// --------------------------------------------------------------------------------------
// ALWAYS_USE_MOVAPS [define] / AlwaysUseMovaps [const]
// --------------------------------------------------------------------------------------
2009-04-20 15:22:02 +00:00
// This tells the recompiler's emitter to always use movaps instead of movdqa. Both instructions
// do the exact same thing, but movaps is 1 byte shorter, and thus results in a cleaner L1 cache
// and some marginal speed gains as a result. (it's possible someday in the future the per-
// formance of the two instructions could change, so this constant is provided to restore MOVDQA
// use easily at a later time, if needed).
2009-11-06 21:45:30 +00:00
//
2009-04-20 15:22:02 +00:00
# define ALWAYS_USE_MOVAPS
# ifdef ALWAYS_USE_MOVAPS
2021-09-06 18:28:26 +00:00
static const bool AlwaysUseMovaps = true ;
2009-04-20 15:22:02 +00:00
# else
2021-09-06 18:28:26 +00:00
static const bool AlwaysUseMovaps = false ;
2009-04-20 15:22:02 +00:00
# endif
2009-11-06 21:45:30 +00:00
// --------------------------------------------------------------------------------------
2009-11-18 16:53:44 +00:00
// __emitline - preprocessors definition
2009-11-06 21:45:30 +00:00
// --------------------------------------------------------------------------------------
2009-10-04 20:28:08 +00:00
// This is configured to inline emitter functions appropriately for release builds, and
2009-04-14 01:26:57 +00:00
// disables some of the more aggressive inlines for dev builds (which can be helpful when
2009-04-19 02:14:50 +00:00
// debugging). Additionally, I've set up the inlining to be as practical and intelligent
// as possible with regard to constant propagation. Namely this involves forcing inlining
// for (void*) forms of ModRM, which (thanks to constprop) reduce to virtually no code, and
// force-disabling inlining on complicated SibSB forms [since MSVC would sometimes inline
// despite being a generally bad idea].
2009-04-14 01:26:57 +00:00
//
2009-04-19 02:14:50 +00:00
// In the case of (Reg, Imm) forms, the inlining is up to the discreation of the compiler.
//
2010-08-09 04:10:38 +00:00
// Note: I *intentionally* use __fi directly for most single-line class members,
2009-04-19 02:14:50 +00:00
// when needed. There's no point in using __emitline in these cases since the debugger
// can't trace into single-line functions anyway.
2009-04-14 01:26:57 +00:00
//
# ifdef PCSX2_DEVBUILD
2016-11-12 15:28:37 +00:00
# define __emitinline
2009-04-14 01:26:57 +00:00
# else
2016-11-12 15:28:37 +00:00
# define __emitinline __fi
2009-04-14 01:26:57 +00:00
# endif
2021-09-06 18:28:26 +00:00
// ModRM 'mod' field enumeration. Provided mostly for reference:
enum ModRm_ModField
{
Mod_NoDisp = 0 , // effective address operation with no displacement, in the form of [reg] (or uses special Disp32-only encoding in the case of [ebp] form)
Mod_Disp8 , // effective address operation with 8 bit displacement, in the form of [reg+disp8]
Mod_Disp32 , // effective address operation with 32 bit displacement, in the form of [reg+disp32],
Mod_Direct , // direct reg/reg operation
} ;
// ----------------------------------------------------------------------------
// JccComparisonType - enumerated possibilities for inspired code branching!
//
enum JccComparisonType
{
Jcc_Unknown = - 2 ,
Jcc_Unconditional = - 1 ,
Jcc_Overflow = 0x0 ,
Jcc_NotOverflow = 0x1 ,
Jcc_Below = 0x2 ,
Jcc_Carry = 0x2 ,
Jcc_AboveOrEqual = 0x3 ,
Jcc_NotCarry = 0x3 ,
Jcc_Zero = 0x4 ,
Jcc_Equal = 0x4 ,
Jcc_NotZero = 0x5 ,
Jcc_NotEqual = 0x5 ,
Jcc_BelowOrEqual = 0x6 ,
Jcc_Above = 0x7 ,
Jcc_Signed = 0x8 ,
Jcc_Unsigned = 0x9 ,
Jcc_ParityEven = 0xa ,
Jcc_ParityOdd = 0xb ,
Jcc_Less = 0xc ,
Jcc_GreaterOrEqual = 0xd ,
Jcc_LessOrEqual = 0xe ,
Jcc_Greater = 0xf ,
} ;
// Not supported yet:
//E3 cb JECXZ rel8 Jump short if ECX register is 0.
// ----------------------------------------------------------------------------
// SSE2_ComparisonType - enumerated possibilities for SIMD data comparison!
//
enum SSE2_ComparisonType
{
SSE2_Equal = 0 ,
SSE2_Less ,
SSE2_LessOrEqual ,
SSE2_Unordered ,
SSE2_NotEqual ,
SSE2_NotLess ,
SSE2_NotLessOrEqual ,
SSE2_Ordered
} ;
static const int ModRm_UseSib = 4 ; // same index value as ESP (used in RM field)
static const int ModRm_UseDisp32 = 5 ; // same index value as EBP (used in Mod field)
static const int Sib_EIZ = 4 ; // same index value as ESP (used in Index field)
static const int Sib_UseDisp32 = 5 ; // same index value as EBP (used in Base field)
extern void xSetPtr ( void * ptr ) ;
extern void xAlignPtr ( uint bytes ) ;
extern void xAdvancePtr ( uint bytes ) ;
extern void xAlignCallTarget ( ) ;
extern u8 * xGetPtr ( ) ;
extern u8 * xGetAlignedCallTarget ( ) ;
extern JccComparisonType xInvertCond ( JccComparisonType src ) ;
class xAddressVoid ;
// --------------------------------------------------------------------------------------
// OperandSizedObject
// --------------------------------------------------------------------------------------
class OperandSizedObject
{
protected :
uint _operandSize = 0 ;
OperandSizedObject ( ) = default ;
OperandSizedObject ( uint operandSize )
: _operandSize ( operandSize )
{
}
public :
uint GetOperandSize ( ) const
{
2023-12-22 10:30:31 +00:00
pxAssertMsg ( _operandSize ! = 0 , " Attempted to use operand size of uninitialized or void object " ) ;
2021-09-06 18:28:26 +00:00
return _operandSize ;
}
bool Is8BitOp ( ) const { return GetOperandSize ( ) = = 1 ; }
u8 GetPrefix16 ( ) const { return GetOperandSize ( ) = = 2 ? 0x66 : 0 ; }
void prefix16 ( ) const
{
if ( GetOperandSize ( ) = = 2 )
xWrite8 ( 0x66 ) ;
}
int GetImmSize ( ) const
{
switch ( GetOperandSize ( ) )
{
case 1 :
return 1 ;
case 2 :
return 2 ;
case 4 :
return 4 ;
case 8 :
return 4 ; // Only mov's take 64-bit immediates
jNO_DEFAULT
}
return 0 ;
}
void xWriteImm ( int imm ) const
{
switch ( GetImmSize ( ) )
{
case 1 :
xWrite8 ( imm ) ;
break ;
case 2 :
xWrite16 ( imm ) ;
break ;
case 4 :
xWrite32 ( imm ) ;
break ;
jNO_DEFAULT
}
}
} ;
// Represents an unused or "empty" register assignment. If encountered by the emitter, this
// will be ignored (in some cases it is disallowed and generates an assertion)
static const int xRegId_Empty = - 1 ;
// Represents an invalid or uninitialized register. If this is encountered by the emitter it
// will generate an assertion.
static const int xRegId_Invalid = - 2 ;
// --------------------------------------------------------------------------------------
// xRegisterBase - type-unsafe x86 register representation.
// --------------------------------------------------------------------------------------
// Unless doing some fundamental stuff, use the friendly xRegister32/16/8 and xRegisterSSE
// instead, which are built using this class and provide strict register type safety when
// passed into emitter instructions.
//
class xRegisterBase : public OperandSizedObject
{
protected :
xRegisterBase ( uint operandSize , int regId )
: OperandSizedObject ( operandSize )
, Id ( regId )
{
// Note: to avoid tons of ifdef, the 32 bits build will instantiate
// all 16x64 bits registers.
pxAssert ( ( Id > = xRegId_Empty ) & & ( Id < 16 ) ) ;
}
public :
int Id ;
xRegisterBase ( )
: OperandSizedObject ( 0 )
, Id ( xRegId_Invalid )
{
}
bool IsEmpty ( ) const { return Id < 0 ; }
bool IsInvalid ( ) const { return Id = = xRegId_Invalid ; }
2022-10-23 08:58:46 +00:00
bool IsExtended ( ) const { return ( Id > = 0 & & ( Id & 0x0F ) > 7 ) ; } // Register 8-15 need an extra bit to be selected
bool IsExtended8Bit ( ) const { return ( Is8BitOp ( ) & & Id > = 0x10 ) ; }
2021-09-06 18:28:26 +00:00
bool IsMem ( ) const { return false ; }
bool IsReg ( ) const { return true ; }
// Returns true if the register is a valid accumulator: Eax, Ax, Al, XMM0.
bool IsAccumulator ( ) const { return Id = = 0 ; }
// IsSIMD: returns true if the register is a valid XMM register.
bool IsSIMD ( ) const { return GetOperandSize ( ) = = 16 ; }
2016-11-12 15:28:37 +00:00
// IsWide: return true if the register is 64 bits (requires a wide op on the rex prefix)
2021-09-06 18:28:26 +00:00
bool IsWide ( ) const
{
return GetOperandSize ( ) = = 8 ;
}
// return true if the register is a valid YMM register
bool IsWideSIMD ( ) const { return GetOperandSize ( ) = = 32 ; }
// Diagnostics -- returns a string representation of this register. Return string
// is a valid non-null string for any Id, valid or invalid. No assertions are generated.
const char * GetName ( ) ;
int GetId ( ) const { return Id ; }
2022-10-23 08:58:46 +00:00
/// Returns true if the specified register is caller-saved (volatile).
static inline bool IsCallerSaved ( uint id ) ;
2021-09-06 18:28:26 +00:00
} ;
class xRegisterInt : public xRegisterBase
{
typedef xRegisterBase _parent ;
protected :
explicit xRegisterInt ( uint operandSize , int regId )
: _parent ( operandSize , regId )
{
}
public :
xRegisterInt ( ) = default ;
/// IDs in [4, 8) are h registers in 8-bit
int isIDSameInAllSizes ( ) const
{
return Id < 4 | | Id > = 8 ;
}
/// Checks if mapping the ID directly would be a good idea
bool canMapIDTo ( int otherSize ) const
{
if ( ( otherSize = = 1 ) = = ( GetOperandSize ( ) = = 1 ) )
return true ;
return isIDSameInAllSizes ( ) ;
}
/// Get a non-wide version of the register (for use with e.g. mov, where `mov eax, 3` and `mov rax, 3` are functionally identical but `mov eax, 3` is shorter)
xRegisterInt GetNonWide ( ) const
{
return GetOperandSize ( ) = = 8 ? xRegisterInt ( 4 , Id ) : * this ;
}
xRegisterInt MatchSizeTo ( xRegisterInt other ) const ;
bool operator = = ( const xRegisterInt & src ) const { return Id = = src . Id & & ( GetOperandSize ( ) = = src . GetOperandSize ( ) ) ; }
bool operator ! = ( const xRegisterInt & src ) const { return ! operator = = ( src ) ; }
} ;
// --------------------------------------------------------------------------------------
// xRegister8/16/32/64 - Represents a basic 8/16/32/64 bit GPR on the x86
// --------------------------------------------------------------------------------------
class xRegister8 : public xRegisterInt
{
typedef xRegisterInt _parent ;
public :
xRegister8 ( ) = default ;
explicit xRegister8 ( int regId )
: _parent ( 1 , regId )
{
}
explicit xRegister8 ( const xRegisterInt & other )
: _parent ( 1 , other . Id )
{
2022-10-23 08:58:46 +00:00
if ( ! other . canMapIDTo ( 1 ) )
Id | = 0x10 ;
}
xRegister8 ( int regId , bool ext8bit )
: _parent ( 1 , regId )
{
if ( ext8bit )
Id | = 0x10 ;
2021-09-06 18:28:26 +00:00
}
bool operator = = ( const xRegister8 & src ) const { return Id = = src . Id ; }
bool operator ! = ( const xRegister8 & src ) const { return Id ! = src . Id ; }
} ;
class xRegister16 : public xRegisterInt
{
typedef xRegisterInt _parent ;
public :
xRegister16 ( ) = default ;
explicit xRegister16 ( int regId )
: _parent ( 2 , regId )
{
}
explicit xRegister16 ( const xRegisterInt & other )
: _parent ( 2 , other . Id )
{
2023-12-22 10:30:31 +00:00
pxAssertMsg ( other . canMapIDTo ( 2 ) , " Mapping h registers to higher registers can produce unexpected values " ) ;
2021-09-06 18:28:26 +00:00
}
bool operator = = ( const xRegister16 & src ) const { return this - > Id = = src . Id ; }
bool operator ! = ( const xRegister16 & src ) const { return this - > Id ! = src . Id ; }
} ;
class xRegister32 : public xRegisterInt
{
typedef xRegisterInt _parent ;
public :
xRegister32 ( ) = default ;
explicit xRegister32 ( int regId )
: _parent ( 4 , regId )
{
}
explicit xRegister32 ( const xRegisterInt & other )
: _parent ( 4 , other . Id )
{
2023-12-22 10:30:31 +00:00
pxAssertMsg ( other . canMapIDTo ( 4 ) , " Mapping h registers to higher registers can produce unexpected values " ) ;
2021-09-06 18:28:26 +00:00
}
2022-12-25 12:14:15 +00:00
static const inline xRegister32 & GetInstance ( uint id ) ;
2021-09-06 18:28:26 +00:00
bool operator = = ( const xRegister32 & src ) const { return this - > Id = = src . Id ; }
bool operator ! = ( const xRegister32 & src ) const { return this - > Id ! = src . Id ; }
} ;
class xRegister64 : public xRegisterInt
{
typedef xRegisterInt _parent ;
public :
xRegister64 ( ) = default ;
explicit xRegister64 ( int regId )
: _parent ( 8 , regId )
{
}
explicit xRegister64 ( const xRegisterInt & other )
: _parent ( 8 , other . Id )
{
2023-12-22 10:30:31 +00:00
pxAssertMsg ( other . canMapIDTo ( 8 ) , " Mapping h registers to higher registers can produce unexpected values " ) ;
2021-09-06 18:28:26 +00:00
}
2022-12-25 12:14:15 +00:00
static const inline xRegister64 & GetInstance ( uint id ) ;
2021-09-06 18:28:26 +00:00
bool operator = = ( const xRegister64 & src ) const { return this - > Id = = src . Id ; }
bool operator ! = ( const xRegister64 & src ) const { return this - > Id ! = src . Id ; }
} ;
// --------------------------------------------------------------------------------------
// xRegisterSSE - Represents either a 64 bit or 128 bit SIMD register
// --------------------------------------------------------------------------------------
// This register type is provided to allow legal syntax for instructions that accept
// an XMM register as a parameter, but do not allow for a GPR.
2022-10-12 07:24:20 +00:00
struct xRegisterYMMTag { } ;
2021-09-06 18:28:26 +00:00
class xRegisterSSE : public xRegisterBase
{
typedef xRegisterBase _parent ;
public :
xRegisterSSE ( ) = default ;
explicit xRegisterSSE ( int regId )
: _parent ( 16 , regId )
{
}
2022-10-12 07:24:20 +00:00
xRegisterSSE ( int regId , xRegisterYMMTag )
: _parent ( 32 , regId )
{
}
2021-09-06 18:28:26 +00:00
bool operator = = ( const xRegisterSSE & src ) const { return this - > Id = = src . Id ; }
bool operator ! = ( const xRegisterSSE & src ) const { return this - > Id ! = src . Id ; }
static const inline xRegisterSSE & GetInstance ( uint id ) ;
2022-10-12 07:24:20 +00:00
static const inline xRegisterSSE & GetYMMInstance ( uint id ) ;
2022-10-16 12:05:45 +00:00
/// Returns the register to use when calling a C function.
/// arg_number is the argument position from the left, starting with 0.
/// sse_number is the argument position relative to the number of vector registers.
static const inline xRegisterSSE & GetArgRegister ( uint arg_number , uint sse_number , bool ymm = false ) ;
2022-10-23 08:58:46 +00:00
/// Returns true if the specified register is caller-saved (volatile).
static inline bool IsCallerSaved ( uint id ) ;
2021-09-06 18:28:26 +00:00
} ;
class xRegisterCL : public xRegister8
{
public :
xRegisterCL ( )
: xRegister8 ( 1 )
{
}
} ;
// --------------------------------------------------------------------------------------
// xAddressReg
// --------------------------------------------------------------------------------------
// Use 32/64 bit registers as our index registers (for ModSib-style memory address calculations).
// This type is implicitly exchangeable with xRegister32/64.
//
// Only xAddressReg provides operators for constructing xAddressInfo types. These operators
// could have been added to xRegister32/64 directly instead, however I think this design makes
// more sense and allows the programmer a little more type protection if needed.
//
2016-01-16 19:20:27 +00:00
# define xRegisterLong xRegister64
2021-09-06 18:28:26 +00:00
static const int wordsize = sizeof ( sptr ) ;
class xAddressReg : public xRegisterLong
{
public :
xAddressReg ( ) = default ;
explicit xAddressReg ( xRegisterInt other )
: xRegisterLong ( other )
{
}
explicit xAddressReg ( int regId )
: xRegisterLong ( regId )
{
}
// Returns true if the register is the stack pointer: ESP.
bool IsStackPointer ( ) const { return Id = = 4 ; }
2022-10-16 12:05:45 +00:00
/// Returns the register to use when calling a C function.
/// arg_number is the argument position from the left, starting with 0.
/// sse_number is the argument position relative to the number of vector registers.
static const inline xAddressReg & GetArgRegister ( uint arg_number , uint gpr_number ) ;
2021-09-06 18:28:26 +00:00
xAddressVoid operator + ( const xAddressReg & right ) const ;
xAddressVoid operator + ( sptr right ) const ;
xAddressVoid operator + ( const void * right ) const ;
xAddressVoid operator - ( sptr right ) const ;
xAddressVoid operator - ( const void * right ) const ;
xAddressVoid operator * ( int factor ) const ;
xAddressVoid operator < < ( u32 shift ) const ;
} ;
// --------------------------------------------------------------------------------------
// xRegisterEmpty
// --------------------------------------------------------------------------------------
struct xRegisterEmpty
{
operator xRegister8 ( ) const
{
return xRegister8 ( xRegId_Empty ) ;
}
operator xRegister16 ( ) const
{
return xRegister16 ( xRegId_Empty ) ;
}
operator xRegister32 ( ) const
{
return xRegister32 ( xRegId_Empty ) ;
}
operator xRegisterSSE ( ) const
{
return xRegisterSSE ( xRegId_Empty ) ;
}
operator xAddressReg ( ) const
{
return xAddressReg ( xRegId_Empty ) ;
}
} ;
class xRegister16or32or64
{
protected :
const xRegisterInt & m_convtype ;
public :
xRegister16or32or64 ( const xRegister64 & src )
: m_convtype ( src )
{
}
xRegister16or32or64 ( const xRegister32 & src )
: m_convtype ( src )
{
}
xRegister16or32or64 ( const xRegister16 & src )
: m_convtype ( src )
{
}
operator const xRegisterBase & ( ) const { return m_convtype ; }
const xRegisterInt * operator - > ( ) const
{
return & m_convtype ;
}
} ;
class xRegister32or64
{
protected :
const xRegisterInt & m_convtype ;
public :
xRegister32or64 ( const xRegister64 & src )
: m_convtype ( src )
{
}
xRegister32or64 ( const xRegister32 & src )
: m_convtype ( src )
{
}
operator const xRegisterBase & ( ) const { return m_convtype ; }
const xRegisterInt * operator - > ( ) const
{
return & m_convtype ;
}
} ;
extern const xRegisterEmpty xEmptyReg ;
// clang-format off
2022-10-12 07:24:20 +00:00
extern const xRegisterSSE
2016-11-12 15:28:37 +00:00
xmm0 , xmm1 , xmm2 , xmm3 ,
xmm4 , xmm5 , xmm6 , xmm7 ,
xmm8 , xmm9 , xmm10 , xmm11 ,
xmm12 , xmm13 , xmm14 , xmm15 ;
2010-07-05 19:15:19 +00:00
2022-10-12 07:24:20 +00:00
// TODO: This needs to be _M_SSE >= 0x500'ed, but we can't do it atm because common doesn't have variants.
extern const xRegisterSSE
ymm0 , ymm1 , ymm2 , ymm3 ,
ymm4 , ymm5 , ymm6 , ymm7 ,
ymm8 , ymm9 , ymm10 , ymm11 ,
ymm12 , ymm13 , ymm14 , ymm15 ;
2016-11-12 15:28:37 +00:00
extern const xAddressReg
rax , rbx , rcx , rdx ,
rsi , rdi , rbp , rsp ,
r8 , r9 , r10 , r11 ,
r12 , r13 , r14 , r15 ;
2009-04-07 08:42:25 +00:00
2020-08-19 08:19:28 +00:00
extern const xRegister32
2020-04-15 21:11:53 +00:00
eax , ebx , ecx , edx ,
esi , edi , ebp , esp ,
2020-08-19 08:19:28 +00:00
r8d , r9d , r10d , r11d ,
r12d , r13d , r14d , r15d ;
2016-11-12 15:28:37 +00:00
extern const xRegister16
ax , bx , cx , dx ,
si , di , bp , sp ;
2009-11-07 15:46:09 +00:00
2016-11-12 15:28:37 +00:00
extern const xRegister8
al , dl , bl ,
2022-10-23 08:58:46 +00:00
ah , ch , dh , bh ,
spl , bpl , sil , dil ,
r8b , r9b , r10b , r11b ,
r12b , r13b , r14b , r15b ;
2009-10-04 20:28:08 +00:00
2020-08-19 08:19:28 +00:00
extern const xAddressReg
arg1reg , arg2reg ,
arg3reg , arg4reg ,
calleeSavedReg1 ,
calleeSavedReg2 ;
extern const xRegister32
arg1regd , arg2regd ,
calleeSavedReg1d ,
calleeSavedReg2d ;
2021-09-06 18:28:26 +00:00
// clang-format on
extern const xRegisterCL cl ; // I'm special!
2022-10-23 08:58:46 +00:00
bool xRegisterBase : : IsCallerSaved ( uint id )
{
# ifdef _WIN32
// The x64 ABI considers the registers RAX, RCX, RDX, R8, R9, R10, R11, and XMM0-XMM5 volatile.
return ( id < = 2 | | ( id > = 8 & & id < = 11 ) ) ;
# else
// rax, rdi, rsi, rdx, rcx, r8, r9, r10, r11 are scratch registers.
return ( id < = 2 | | id = = 6 | | id = = 7 | | ( id > = 8 & & id < = 11 ) ) ;
# endif
}
2022-12-25 12:14:15 +00:00
const xRegister32 & xRegister32 : : GetInstance ( uint id )
{
static const xRegister32 * const m_tbl_x86Regs [ ] =
{
& eax , & ecx , & edx , & ebx ,
& esp , & ebp , & esi , & edi ,
& r8d , & r9d , & r10d , & r11d ,
& r12d , & r13d , & r14d , & r15d ,
} ;
pxAssert ( id < iREGCNT_GPR ) ;
return * m_tbl_x86Regs [ id ] ;
}
const xRegister64 & xRegister64 : : GetInstance ( uint id )
{
static const xRegister64 * const m_tbl_x86Regs [ ] =
{
& rax , & rcx , & rdx , & rbx ,
& rsp , & rbp , & rsi , & rdi ,
& r8 , & r9 , & r10 , & r11 ,
& r12 , & r13 , & r14 , & r15
} ;
pxAssert ( id < iREGCNT_GPR ) ;
return * m_tbl_x86Regs [ id ] ;
}
2022-10-23 08:58:46 +00:00
bool xRegisterSSE : : IsCallerSaved ( uint id )
{
# ifdef _WIN32
// XMM6 through XMM15 are saved. Upper 128 bits is always volatile.
return ( id < 6 ) ;
# else
// All vector registers are volatile.
return true ;
# endif
}
2021-09-06 18:28:26 +00:00
const xRegisterSSE & xRegisterSSE : : GetInstance ( uint id )
{
static const xRegisterSSE * const m_tbl_xmmRegs [ ] =
{
& xmm0 , & xmm1 , & xmm2 , & xmm3 ,
& xmm4 , & xmm5 , & xmm6 , & xmm7 ,
& xmm8 , & xmm9 , & xmm10 , & xmm11 ,
& xmm12 , & xmm13 , & xmm14 , & xmm15 } ;
pxAssert ( id < iREGCNT_XMM ) ;
return * m_tbl_xmmRegs [ id ] ;
}
2022-10-12 07:24:20 +00:00
const xRegisterSSE & xRegisterSSE : : GetYMMInstance ( uint id )
{
static const xRegisterSSE * const m_tbl_ymmRegs [ ] =
{
& ymm0 , & ymm1 , & ymm2 , & ymm3 ,
& ymm4 , & ymm5 , & ymm6 , & ymm7 ,
& ymm8 , & ymm9 , & ymm10 , & ymm11 ,
& ymm12 , & ymm13 , & ymm14 , & ymm15 } ;
pxAssert ( id < iREGCNT_XMM ) ;
return * m_tbl_ymmRegs [ id ] ;
}
2022-10-16 12:05:45 +00:00
const xRegisterSSE & xRegisterSSE : : GetArgRegister ( uint arg_number , uint sse_number , bool ymm )
{
# ifdef _WIN32
// Windows passes arguments according to their position from the left.
return ymm ? GetYMMInstance ( arg_number ) : GetInstance ( arg_number ) ;
# else
// Linux counts the number of vector parameters.
return ymm ? GetYMMInstance ( sse_number ) : GetInstance ( sse_number ) ;
# endif
}
const xAddressReg & xAddressReg : : GetArgRegister ( uint arg_number , uint gpr_number )
{
# ifdef _WIN32
// Windows passes arguments according to their position from the left.
static constexpr const xAddressReg * regs [ ] = { & rcx , & rdx , & r8 , & r9 } ;
pxAssert ( arg_number < std : : size ( regs ) ) ;
return * regs [ arg_number ] ;
# else
// Linux counts the number of GPR parameters.
static constexpr const xAddressReg * regs [ ] = { & rdi , & rsi , & rdx , & rcx } ;
pxAssert ( gpr_number < std : : size ( regs ) ) ;
return * regs [ gpr_number ] ;
# endif
}
2021-09-06 18:28:26 +00:00
// --------------------------------------------------------------------------------------
// xAddressVoid
// --------------------------------------------------------------------------------------
class xAddressVoid
{
public :
xAddressReg Base ; // base register (no scale)
xAddressReg Index ; // index reg gets multiplied by the scale
int Factor ; // scale applied to the index register, in factor form (not a shift!)
sptr Displacement ; // address displacement // 4B max even on 64 bits but keep rest for assertions
public :
xAddressVoid ( const xAddressReg & base , const xAddressReg & index , int factor = 1 , sptr displacement = 0 ) ;
xAddressVoid ( const xAddressReg & index , sptr displacement = 0 ) ;
explicit xAddressVoid ( const void * displacement ) ;
explicit xAddressVoid ( sptr displacement = 0 ) ;
public :
bool IsByteSizeDisp ( ) const { return is_s8 ( Displacement ) ; }
xAddressVoid & Add ( sptr imm )
{
Displacement + = imm ;
return * this ;
}
xAddressVoid & Add ( const xAddressReg & src ) ;
xAddressVoid & Add ( const xAddressVoid & src ) ;
__fi xAddressVoid operator + ( const xAddressReg & right ) const { return xAddressVoid ( * this ) . Add ( right ) ; }
__fi xAddressVoid operator + ( const xAddressVoid & right ) const { return xAddressVoid ( * this ) . Add ( right ) ; }
__fi xAddressVoid operator + ( sptr imm ) const { return xAddressVoid ( * this ) . Add ( imm ) ; }
__fi xAddressVoid operator - ( sptr imm ) const { return xAddressVoid ( * this ) . Add ( - imm ) ; }
__fi xAddressVoid operator + ( const void * addr ) const { return xAddressVoid ( * this ) . Add ( ( uptr ) addr ) ; }
__fi void operator + = ( const xAddressReg & right ) { Add ( right ) ; }
__fi void operator + = ( sptr imm ) { Add ( imm ) ; }
__fi void operator - = ( sptr imm ) { Add ( - imm ) ; }
} ;
static __fi xAddressVoid operator + ( const void * addr , const xAddressVoid & right )
{
return right + addr ;
}
static __fi xAddressVoid operator + ( sptr addr , const xAddressVoid & right )
{
return right + addr ;
}
// --------------------------------------------------------------------------------------
// xImmReg< typename xRegType >
// --------------------------------------------------------------------------------------
// Used to represent an immediate value which can also be optimized to a register. Note
// that the immediate value represented by this structure is *always* legal. The register
// assignment is an optional optimization which can be implemented in cases where an
// immediate is used enough times to merit allocating it to a register.
//
// Note: not all instructions support this operand type (yet). You can always implement it
// manually by checking the status of IsReg() and generating the xOP conditionally.
//
template < typename xRegType >
class xImmReg
{
xRegType m_reg ;
int m_imm ;
public :
xImmReg ( )
: m_reg ( )
{
m_imm = 0 ;
}
xImmReg ( int imm , const xRegType & reg = xEmptyReg )
{
m_reg = reg ;
m_imm = imm ;
}
const xRegType & GetReg ( ) const { return m_reg ; }
int GetImm ( ) const { return m_imm ; }
bool IsReg ( ) const { return ! m_reg . IsEmpty ( ) ; }
} ;
// --------------------------------------------------------------------------------------
// xIndirectVoid - Internal low-level representation of the ModRM/SIB information.
// --------------------------------------------------------------------------------------
// This class serves two purposes: It houses 'reduced' ModRM/SIB info only, which means
// that the Base, Index, Scale, and Displacement values are all in the correct arrange-
// ments, and it serves as a type-safe layer between the xRegister's operators (which
// generate xAddressInfo types) and the emitter's ModSib instruction forms. Without this,
// the xRegister would pass as a ModSib type implicitly, and that would cause ambiguity
// on a number of instructions.
//
// End users should always use xAddressInfo instead.
//
class xIndirectVoid : public OperandSizedObject
{
public :
xAddressReg Base ; // base register (no scale)
xAddressReg Index ; // index reg gets multiplied by the scale
uint Scale ; // scale applied to the index register, in scale/shift form
sptr Displacement ; // offset applied to the Base/Index registers.
// Displacement is 8/32 bits even on x86_64
// However we need the whole pointer to calculate rip-relative offsets
public :
explicit xIndirectVoid ( sptr disp ) ;
explicit xIndirectVoid ( const xAddressVoid & src ) ;
xIndirectVoid ( xAddressReg base , xAddressReg index , int scale = 0 , sptr displacement = 0 ) ;
xIndirectVoid & Add ( sptr imm ) ;
bool IsByteSizeDisp ( ) const { return is_s8 ( Displacement ) ; }
bool IsMem ( ) const { return true ; }
bool IsReg ( ) const { return false ; }
bool IsExtended ( ) const { return false ; } // Non sense but ease template
2022-11-06 07:22:00 +00:00
bool IsWide ( ) const { return _operandSize = = 8 ; }
2021-09-06 18:28:26 +00:00
operator xAddressVoid ( )
{
return xAddressVoid ( Base , Index , Scale , Displacement ) ;
}
__fi xIndirectVoid operator + ( const sptr imm ) const { return xIndirectVoid ( * this ) . Add ( imm ) ; }
__fi xIndirectVoid operator - ( const sptr imm ) const { return xIndirectVoid ( * this ) . Add ( - imm ) ; }
protected :
void Reduce ( ) ;
} ;
template < typename OperandType >
class xIndirect : public xIndirectVoid
{
typedef xIndirectVoid _parent ;
public :
explicit xIndirect ( sptr disp )
: _parent ( disp )
{
_operandSize = sizeof ( OperandType ) ;
}
xIndirect ( xAddressReg base , xAddressReg index , int scale = 0 , sptr displacement = 0 )
: _parent ( base , index , scale , displacement )
{
_operandSize = sizeof ( OperandType ) ;
}
explicit xIndirect ( const xIndirectVoid & other )
: _parent ( other )
{
}
xIndirect < OperandType > & Add ( sptr imm )
{
Displacement + = imm ;
return * this ;
}
__fi xIndirect < OperandType > operator + ( const sptr imm ) const { return xIndirect ( * this ) . Add ( imm ) ; }
__fi xIndirect < OperandType > operator - ( const sptr imm ) const { return xIndirect ( * this ) . Add ( - imm ) ; }
bool operator = = ( const xIndirect < OperandType > & src ) const
{
return ( Base = = src . Base ) & & ( Index = = src . Index ) & &
( Scale = = src . Scale ) & & ( Displacement = = src . Displacement ) ;
}
bool operator ! = ( const xIndirect < OperandType > & src ) const
{
return ! operator = = ( src ) ;
}
protected :
void Reduce ( ) ;
} ;
typedef xIndirect < u128 > xIndirect128 ;
typedef xIndirect < u64 > xIndirect64 ;
typedef xIndirect < u32 > xIndirect32 ;
typedef xIndirect < u16 > xIndirect16 ;
typedef xIndirect < u8 > xIndirect8 ;
typedef xIndirect < u64 > xIndirectNative ;
2009-04-14 01:26:57 +00:00
2021-09-06 18:28:26 +00:00
// --------------------------------------------------------------------------------------
// xIndirect64orLess - base class 64, 32, 16, and 8 bit operand types
// --------------------------------------------------------------------------------------
class xIndirect64orLess : public xIndirectVoid
{
typedef xIndirectVoid _parent ;
public :
xIndirect64orLess ( const xIndirect8 & src )
: _parent ( src )
{
}
xIndirect64orLess ( const xIndirect16 & src )
: _parent ( src )
{
}
xIndirect64orLess ( const xIndirect32 & src )
: _parent ( src )
{
}
xIndirect64orLess ( const xIndirect64 & src )
: _parent ( src )
{
}
} ;
// --------------------------------------------------------------------------------------
// xAddressIndexer
// --------------------------------------------------------------------------------------
// This is a type-translation "interface class" which provisions our ptr[] syntax.
// xAddressReg types go in, and xIndirectVoid derived types come out.
//
template < typename xModSibType >
class xAddressIndexer
{
public :
// passthrough instruction, allows ModSib to pass silently through ptr translation
// without doing anything and without compiler error.
const xModSibType & operator [ ] ( const xModSibType & src ) const { return src ; }
xModSibType operator [ ] ( const xAddressReg & src ) const
{
return xModSibType ( src , xEmptyReg ) ;
}
xModSibType operator [ ] ( const xAddressVoid & src ) const
{
return xModSibType ( src . Base , src . Index , src . Factor , src . Displacement ) ;
}
xModSibType operator [ ] ( const void * src ) const
{
return xModSibType ( ( uptr ) src ) ;
}
} ;
// ptr[] - use this form for instructions which can resolve the address operand size from
// the other register operand sizes.
extern const xAddressIndexer < xIndirectVoid > ptr ;
extern const xAddressIndexer < xIndirectNative > ptrNative ;
extern const xAddressIndexer < xIndirect128 > ptr128 ;
extern const xAddressIndexer < xIndirect64 > ptr64 ;
extern const xAddressIndexer < xIndirect32 > ptr32 ;
extern const xAddressIndexer < xIndirect16 > ptr16 ;
extern const xAddressIndexer < xIndirect8 > ptr8 ;
// --------------------------------------------------------------------------------------
// xForwardJump
// --------------------------------------------------------------------------------------
// Primary use of this class is through the various xForwardJA8/xForwardJLE32/etc. helpers
// defined later in this header. :)
//
class xForwardJumpBase
{
public :
// pointer to base of the instruction *Following* the jump. The jump address will be
// relative to this address.
s8 * BasePtr ;
public :
xForwardJumpBase ( uint opsize , JccComparisonType cctype ) ;
protected :
void _setTarget ( uint opsize ) const ;
} ;
template < typename OperandType >
class xForwardJump : public xForwardJumpBase
{
public :
static const uint OperandSize = sizeof ( OperandType ) ;
// The jump instruction is emitted at the point of object construction. The conditional
// type must be valid (Jcc_Unknown generates an assertion).
xForwardJump ( JccComparisonType cctype = Jcc_Unconditional )
: xForwardJumpBase ( OperandSize , cctype )
{
}
// Sets the jump target by writing back the current x86Ptr to the jump instruction.
// This method can be called multiple times, re-writing the jump instruction's target
// in each case. (the the last call is the one that takes effect).
void SetTarget ( ) const
{
_setTarget ( OperandSize ) ;
}
} ;
static __fi xAddressVoid operator + ( const void * addr , const xAddressReg & reg )
{
return reg + ( sptr ) addr ;
}
static __fi xAddressVoid operator + ( sptr addr , const xAddressReg & reg )
{
return reg + ( sptr ) addr ;
}
} // namespace x86Emitter
2009-04-14 01:26:57 +00:00
2009-11-06 21:45:30 +00:00
# include "implement/helpers.h"
2009-10-24 19:06:11 +00:00
# include "implement/simd_helpers.h"
2009-11-05 23:39:45 +00:00
# include "implement/simd_moremovs.h"
2009-10-24 19:06:11 +00:00
# include "implement/simd_arithmetic.h"
2009-10-24 21:43:28 +00:00
# include "implement/simd_comparisons.h"
2009-11-05 23:39:45 +00:00
# include "implement/simd_shufflepack.h"
2009-10-24 19:06:11 +00:00
2009-11-06 21:45:30 +00:00
# include "implement/group1.h"
# include "implement/group2.h"
# include "implement/group3.h"
2021-09-06 18:28:26 +00:00
# include "implement/movs.h" // cmov and movsx/zx
2016-11-12 15:28:37 +00:00
# include "implement/dwshift.h" // doubleword shifts!
2009-11-06 21:45:30 +00:00
# include "implement/incdec.h"
# include "implement/test.h"
# include "implement/jmpcall.h"
2015-12-09 22:51:51 +00:00
# include "implement/bmi.h"
2022-10-12 07:24:20 +00:00
# include "implement/avx.h"