2009-09-08 12:08:10 +00:00
|
|
|
/* PCSX2 - PS2 Emulator for PCs
|
2010-05-03 14:08:02 +00:00
|
|
|
* Copyright (C) 2002-2010 PCSX2 Dev Team
|
2009-10-04 20:28:08 +00:00
|
|
|
*
|
2009-09-08 12:08:10 +00:00
|
|
|
* PCSX2 is free software: you can redistribute it and/or modify it under the terms
|
|
|
|
* of the GNU Lesser General Public License as published by the Free Software Found-
|
|
|
|
* ation, either version 3 of the License, or (at your option) any later version.
|
2009-02-09 21:15:56 +00:00
|
|
|
*
|
2009-09-08 12:08:10 +00:00
|
|
|
* PCSX2 is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
|
|
|
|
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
|
|
|
|
* PURPOSE. See the GNU General Public License for more details.
|
2009-07-03 20:12:33 +00:00
|
|
|
*
|
2009-09-08 12:08:10 +00:00
|
|
|
* You should have received a copy of the GNU General Public License along with PCSX2.
|
|
|
|
* If not, see <http://www.gnu.org/licenses/>.
|
2009-02-09 21:15:56 +00:00
|
|
|
*/
|
2009-04-14 01:26:57 +00:00
|
|
|
|
2009-02-09 21:15:56 +00:00
|
|
|
/*
|
2009-11-06 21:45:30 +00:00
|
|
|
* ix86 core v0.9.1
|
2009-04-14 01:26:57 +00:00
|
|
|
*
|
|
|
|
* Original Authors (v0.6.2 and prior):
|
|
|
|
* linuzappz <linuzappz@pcsx.net>
|
|
|
|
* alexey silinov
|
|
|
|
* goldfinger
|
|
|
|
* zerofrog(@gmail.com)
|
|
|
|
*
|
2009-11-06 21:45:30 +00:00
|
|
|
* Authors of v0.9.1:
|
2009-04-14 01:26:57 +00:00
|
|
|
* Jake.Stine(@gmail.com)
|
|
|
|
* cottonvibes(@gmail.com)
|
|
|
|
* sudonim(1@gmail.com)
|
2009-02-09 21:15:56 +00:00
|
|
|
*/
|
|
|
|
|
|
|
|
#include "PrecompiledHeader.h"
|
2009-07-03 00:49:40 +00:00
|
|
|
#include "internal.h"
|
2009-10-22 13:00:59 +00:00
|
|
|
#include "tools.h"
|
2009-02-09 21:15:56 +00:00
|
|
|
|
2009-04-14 01:26:57 +00:00
|
|
|
// ------------------------------------------------------------------------
|
|
|
|
// Notes on Thread Local Storage:
|
|
|
|
// * TLS is pretty simple, and "just works" from a programmer perspective, with only
|
|
|
|
// some minor additional computational overhead (see performance notes below).
|
|
|
|
//
|
|
|
|
// * MSVC and GCC handle TLS differently internally, but behavior to the programmer is
|
|
|
|
// generally identical.
|
|
|
|
//
|
|
|
|
// Performance Considerations:
|
2009-10-16 03:46:19 +00:00
|
|
|
// * GCC's implementation involves an extra dereference from normal storage (possibly
|
|
|
|
// applies to x86-32 only -- x86-64 is untested).
|
2009-04-14 01:26:57 +00:00
|
|
|
//
|
|
|
|
// * MSVC's implementation involves *two* extra dereferences from normal storage because
|
|
|
|
// it has to look up the TLS heap pointer from the Windows Thread Storage Area. (in
|
2009-10-16 03:46:19 +00:00
|
|
|
// generated ASM code, this dereference is denoted by access to the fs:[2ch] address),
|
2009-04-14 01:26:57 +00:00
|
|
|
//
|
|
|
|
// * However, in either case, the optimizer usually optimizes it to a register so the
|
2009-10-16 03:46:19 +00:00
|
|
|
// extra overhead is minimal over a series of instructions.
|
|
|
|
//
|
|
|
|
// MSVC Notes:
|
|
|
|
// * Important!! the Full Optimization [/Ox] option effectively disables TLS optimizations
|
|
|
|
// in MSVC 2008 and earlier, causing generally significant code bloat. Not tested in
|
|
|
|
// VC2010 yet.
|
|
|
|
//
|
|
|
|
// * VC2010 generally does a superior job of optimizing TLS across inlined functions and
|
|
|
|
// class methods, compared to predecessors.
|
2009-04-14 01:26:57 +00:00
|
|
|
//
|
|
|
|
|
|
|
|
|
2009-12-24 22:22:34 +00:00
|
|
|
__tls_emit u8* x86Ptr;
|
|
|
|
__tls_emit XMMSSEType g_xmmtypes[iREGCNT_XMM] = { XMMT_INT };
|
2009-04-07 08:42:25 +00:00
|
|
|
|
2009-04-07 21:54:50 +00:00
|
|
|
namespace x86Emitter {
|
|
|
|
|
2009-11-06 21:45:30 +00:00
|
|
|
template void xWrite<u8>( u8 val );
|
|
|
|
template void xWrite<u16>( u16 val );
|
|
|
|
template void xWrite<u32>( u32 val );
|
|
|
|
template void xWrite<u64>( u64 val );
|
|
|
|
template void xWrite<u128>( u128 val );
|
|
|
|
|
2010-08-09 04:10:38 +00:00
|
|
|
__fi void xWrite8( u8 val )
|
2009-04-24 11:25:10 +00:00
|
|
|
{
|
|
|
|
xWrite( val );
|
|
|
|
}
|
|
|
|
|
2010-08-09 04:10:38 +00:00
|
|
|
__fi void xWrite16( u16 val )
|
2009-07-03 20:12:33 +00:00
|
|
|
{
|
2009-04-24 11:25:10 +00:00
|
|
|
xWrite( val );
|
2009-07-03 20:12:33 +00:00
|
|
|
}
|
2009-04-24 11:25:10 +00:00
|
|
|
|
2010-08-09 04:10:38 +00:00
|
|
|
__fi void xWrite32( u32 val )
|
2009-07-03 20:12:33 +00:00
|
|
|
{
|
2009-04-24 11:25:10 +00:00
|
|
|
xWrite( val );
|
2009-07-03 20:12:33 +00:00
|
|
|
}
|
2009-04-24 11:25:10 +00:00
|
|
|
|
2010-08-09 04:10:38 +00:00
|
|
|
__fi void xWrite64( u64 val )
|
2009-07-03 20:12:33 +00:00
|
|
|
{
|
2009-04-24 11:25:10 +00:00
|
|
|
xWrite( val );
|
|
|
|
}
|
|
|
|
|
2009-11-07 15:46:09 +00:00
|
|
|
// Empty initializers are due to frivolously pointless GCC errors (it demands the
|
|
|
|
// objects be initialized even though they have no actual variable members).
|
|
|
|
|
2010-07-05 19:15:19 +00:00
|
|
|
const xAddressIndexer<xIndirectVoid> ptr = { };
|
|
|
|
const xAddressIndexer<xIndirect128> ptr128 = { };
|
|
|
|
const xAddressIndexer<xIndirect64> ptr64 = { };
|
|
|
|
const xAddressIndexer<xIndirect32> ptr32 = { };
|
|
|
|
const xAddressIndexer<xIndirect16> ptr16 = { };
|
|
|
|
const xAddressIndexer<xIndirect8> ptr8 = { };
|
2009-04-07 21:54:50 +00:00
|
|
|
|
2009-04-14 01:26:57 +00:00
|
|
|
// ------------------------------------------------------------------------
|
2009-04-14 12:37:48 +00:00
|
|
|
|
2009-11-07 15:46:09 +00:00
|
|
|
const xRegisterEmpty xEmptyReg = { };
|
2009-04-19 05:24:20 +00:00
|
|
|
|
2009-04-20 03:10:05 +00:00
|
|
|
const xRegisterSSE
|
2009-04-19 05:24:20 +00:00
|
|
|
xmm0( 0 ), xmm1( 1 ),
|
|
|
|
xmm2( 2 ), xmm3( 3 ),
|
|
|
|
xmm4( 4 ), xmm5( 5 ),
|
2016-01-16 19:26:53 +00:00
|
|
|
xmm6( 6 ), xmm7( 7 ),
|
|
|
|
xmm8( 8 ), xmm9( 9 ),
|
|
|
|
xmm10( 10 ), xmm11( 11 ),
|
|
|
|
xmm12( 12 ), xmm13( 13 ),
|
|
|
|
xmm14( 14 ), xmm15( 15 );
|
2009-04-19 05:24:20 +00:00
|
|
|
|
2016-01-16 19:26:53 +00:00
|
|
|
const xAddressReg
|
|
|
|
rax( 0 ), rbx( 3 ),
|
|
|
|
rcx( 1 ), rdx( 2 ),
|
|
|
|
rsp( 4 ), rbp( 5 ),
|
|
|
|
rsi( 6 ), rdi( 7 ),
|
|
|
|
r8( 8 ) , r9( 9 ) ,
|
|
|
|
r10( 10 ), r11( 11 ),
|
|
|
|
r12( 12 ), r13( 13 ),
|
|
|
|
r14( 14 ), r15( 15 );
|
|
|
|
|
2009-04-22 18:35:44 +00:00
|
|
|
const xAddressReg
|
2009-04-14 01:26:57 +00:00
|
|
|
eax( 0 ), ebx( 3 ),
|
|
|
|
ecx( 1 ), edx( 2 ),
|
2009-07-03 00:49:40 +00:00
|
|
|
esp( 4 ), ebp( 5 ),
|
|
|
|
esi( 6 ), edi( 7 );
|
2009-04-14 01:26:57 +00:00
|
|
|
|
2009-04-20 03:10:05 +00:00
|
|
|
const xRegister16
|
2009-04-14 01:26:57 +00:00
|
|
|
ax( 0 ), bx( 3 ),
|
|
|
|
cx( 1 ), dx( 2 ),
|
2009-07-03 00:49:40 +00:00
|
|
|
sp( 4 ), bp( 5 ),
|
|
|
|
si( 6 ), di( 7 );
|
2009-04-14 01:26:57 +00:00
|
|
|
|
2009-04-20 03:10:05 +00:00
|
|
|
const xRegister8
|
2009-04-16 01:34:09 +00:00
|
|
|
al( 0 ),
|
2009-04-14 01:26:57 +00:00
|
|
|
dl( 2 ), bl( 3 ),
|
|
|
|
ah( 4 ), ch( 5 ),
|
|
|
|
dh( 6 ), bh( 7 );
|
2009-07-03 20:12:33 +00:00
|
|
|
|
2009-04-20 03:10:05 +00:00
|
|
|
const xRegisterCL cl;
|
2009-04-14 01:26:57 +00:00
|
|
|
|
2016-01-21 21:34:13 +00:00
|
|
|
const char *const x86_regnames_gpr8[] =
|
2009-07-03 00:49:40 +00:00
|
|
|
{
|
|
|
|
"al", "cl", "dl", "bl",
|
2016-03-28 16:03:08 +00:00
|
|
|
"ah", "ch", "dh", "bh",
|
2016-01-21 21:34:13 +00:00
|
|
|
"b8", "b9", "b10", "b11",
|
|
|
|
"b12", "b13", "b14", "b15"
|
2009-07-03 00:49:40 +00:00
|
|
|
};
|
|
|
|
|
2016-01-16 19:26:53 +00:00
|
|
|
const char *const x86_regnames_gpr16[] =
|
2009-07-03 00:49:40 +00:00
|
|
|
{
|
|
|
|
"ax", "cx", "dx", "bx",
|
2016-03-28 16:03:08 +00:00
|
|
|
"sp", "bp", "si", "di",
|
2016-01-21 21:34:13 +00:00
|
|
|
"h8", "h9", "h10", "h11",
|
|
|
|
"h12", "h13", "h14", "h15"
|
2009-07-03 00:49:40 +00:00
|
|
|
};
|
|
|
|
|
2016-01-16 19:26:53 +00:00
|
|
|
const char *const x86_regnames_gpr32[] =
|
2009-07-03 00:49:40 +00:00
|
|
|
{
|
|
|
|
"eax", "ecx", "edx", "ebx",
|
2016-01-16 19:26:53 +00:00
|
|
|
"esp", "ebp", "esi", "edi",
|
|
|
|
"e8", "e9", "e10", "e11",
|
2016-01-21 21:34:13 +00:00
|
|
|
"e12", "e13", "e14", "e15"
|
2016-01-16 19:26:53 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
const char *const x86_regnames_gpr64[] =
|
|
|
|
{
|
|
|
|
"rax", "rcx", "rdx", "rbx",
|
|
|
|
"rsp", "rbp", "rsi", "rdi",
|
2016-01-21 21:34:13 +00:00
|
|
|
"r8" , "r9" , "r10", "r11",
|
|
|
|
"r12", "r13", "r14", "r15"
|
2009-07-03 00:49:40 +00:00
|
|
|
};
|
|
|
|
|
2016-01-16 19:26:53 +00:00
|
|
|
const char *const x86_regnames_sse[] =
|
2009-07-03 00:49:40 +00:00
|
|
|
{
|
|
|
|
"xmm0", "xmm1", "xmm2", "xmm3",
|
2016-01-16 19:26:53 +00:00
|
|
|
"xmm4", "xmm5", "xmm6", "xmm7",
|
2016-01-21 21:34:13 +00:00
|
|
|
"xmm8", "xmm9", "xmm10", "xmm11",
|
|
|
|
"xmm12", "xmm13", "xmm14", "xmm15"
|
2009-07-03 00:49:40 +00:00
|
|
|
};
|
|
|
|
|
2009-11-06 21:45:30 +00:00
|
|
|
const char* xRegisterBase::GetName()
|
2009-04-07 08:42:25 +00:00
|
|
|
{
|
2009-11-06 21:45:30 +00:00
|
|
|
if( Id == xRegId_Invalid ) return "invalid";
|
|
|
|
if( Id == xRegId_Empty ) return "empty";
|
2009-11-06 21:51:39 +00:00
|
|
|
|
2009-11-06 21:45:30 +00:00
|
|
|
// bad error? Return a "big" error string. Might break formatting of register tables
|
|
|
|
// but that's the least of your worries if you see this baby.
|
2016-01-26 17:42:53 +00:00
|
|
|
if( Id >= (int)iREGCNT_GPR || Id < 0 ) return "!Register index out of range!";
|
2009-11-06 21:45:30 +00:00
|
|
|
|
|
|
|
switch( GetOperandSize() )
|
2009-07-03 00:49:40 +00:00
|
|
|
{
|
2009-11-06 21:45:30 +00:00
|
|
|
case 1: return x86_regnames_gpr8[ Id ];
|
|
|
|
case 2: return x86_regnames_gpr16[ Id ];
|
|
|
|
case 4: return x86_regnames_gpr32[ Id ];
|
2016-01-16 19:26:53 +00:00
|
|
|
#ifdef __x86_64__
|
|
|
|
case 8: return x86_regnames_gpr64[ Id ];
|
|
|
|
#endif
|
2009-11-06 21:45:30 +00:00
|
|
|
case 16: return x86_regnames_sse[ Id ];
|
2009-07-03 00:49:40 +00:00
|
|
|
}
|
|
|
|
|
2009-11-06 21:45:30 +00:00
|
|
|
return "oops?";
|
|
|
|
}
|
2009-04-16 14:45:13 +00:00
|
|
|
|
2009-11-06 21:45:30 +00:00
|
|
|
//////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
// Performance note: VC++ wants to use byte/word register form for the following
|
|
|
|
// ModRM/SibSB constructors when we use xWrite<u8>, and furthermore unrolls the
|
|
|
|
// the shift using a series of ADDs for the following results:
|
|
|
|
// add cl,cl
|
|
|
|
// add cl,cl
|
|
|
|
// add cl,cl
|
|
|
|
// or cl,bl
|
|
|
|
// add cl,cl
|
|
|
|
// ... etc.
|
|
|
|
//
|
|
|
|
// This is unquestionably bad optimization by Core2 standard, an generates tons of
|
|
|
|
// register aliases and false dependencies. (although may have been ideal for early-
|
|
|
|
// brand P4s with a broken barrel shifter?). The workaround is to do our own manual
|
|
|
|
// x86Ptr access and update using a u32 instead of u8. Thanks to little endianness,
|
|
|
|
// the same end result is achieved and no false dependencies are generated. The draw-
|
|
|
|
// back is that it clobbers 3 bytes past the end of the write, which could cause a
|
|
|
|
// headache for someone who himself is doing some kind of headache-inducing amount of
|
|
|
|
// recompiler SMC. So we don't do a work-around, and just hope for the compiler to
|
|
|
|
// stop sucking someday instead. :)
|
|
|
|
//
|
|
|
|
// (btw, I know this isn't a critical performance item by any means, but it's
|
|
|
|
// annoying simply because it *should* be an easy thing to optimize)
|
2009-04-15 21:00:32 +00:00
|
|
|
|
2010-08-09 04:10:38 +00:00
|
|
|
static __fi void ModRM( uint mod, uint reg, uint rm )
|
2009-11-06 21:45:30 +00:00
|
|
|
{
|
|
|
|
xWrite8( (mod << 6) | (reg << 3) | rm );
|
|
|
|
}
|
2009-04-15 21:00:32 +00:00
|
|
|
|
2010-08-09 04:10:38 +00:00
|
|
|
static __fi void SibSB( u32 ss, u32 index, u32 base )
|
2009-11-06 21:45:30 +00:00
|
|
|
{
|
|
|
|
xWrite8( (ss << 6) | (index << 3) | base );
|
|
|
|
}
|
2009-04-23 12:39:59 +00:00
|
|
|
|
2009-11-06 21:45:30 +00:00
|
|
|
void EmitSibMagic( uint regfield, const void* address )
|
|
|
|
{
|
|
|
|
ModRM( 0, regfield, ModRm_UseDisp32 );
|
2014-07-30 23:18:10 +00:00
|
|
|
|
|
|
|
// SIB encoding only supports 32bit offsets, even on x86_64
|
|
|
|
// We must make sure that the displacement is within the 32bit range
|
|
|
|
// Else we will fail out in a spectacular fashion
|
|
|
|
sptr displacement = (sptr)address;
|
2016-08-12 17:17:35 +00:00
|
|
|
#ifdef __x86_64__
|
2014-07-30 23:18:10 +00:00
|
|
|
pxAssertDev(displacement >= -0x80000000LL && displacement < 0x80000000LL, "SIB target is too far away, needs an indirect register");
|
2016-08-12 17:17:35 +00:00
|
|
|
#endif
|
2014-07-30 23:18:10 +00:00
|
|
|
|
|
|
|
xWrite<s32>( (s32)displacement );
|
2009-11-06 21:45:30 +00:00
|
|
|
}
|
2009-04-23 12:39:59 +00:00
|
|
|
|
2009-11-06 21:45:30 +00:00
|
|
|
//////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
// returns TRUE if this instruction requires SIB to be encoded, or FALSE if the
|
|
|
|
// instruction ca be encoded as ModRm alone.
|
2010-08-09 04:10:38 +00:00
|
|
|
static __fi bool NeedsSibMagic( const xIndirectVoid& info )
|
2009-11-06 21:45:30 +00:00
|
|
|
{
|
|
|
|
// no registers? no sibs!
|
2010-07-05 19:15:19 +00:00
|
|
|
// (xIndirectVoid::Reduce always places a register in Index, and optionally leaves
|
2009-11-06 21:45:30 +00:00
|
|
|
// Base empty if only register is specified)
|
|
|
|
if( info.Index.IsEmpty() ) return false;
|
2009-04-14 01:26:57 +00:00
|
|
|
|
2009-11-06 21:45:30 +00:00
|
|
|
// A scaled register needs a SIB
|
|
|
|
if( info.Scale != 0 ) return true;
|
2009-04-14 01:26:57 +00:00
|
|
|
|
2009-11-06 21:45:30 +00:00
|
|
|
// two registers needs a SIB
|
|
|
|
if( !info.Base.IsEmpty() ) return true;
|
2009-04-14 01:26:57 +00:00
|
|
|
|
2009-11-06 21:45:30 +00:00
|
|
|
return false;
|
|
|
|
}
|
2009-04-14 01:26:57 +00:00
|
|
|
|
2009-11-06 21:45:30 +00:00
|
|
|
//////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
// Conditionally generates Sib encoding information!
|
|
|
|
//
|
|
|
|
// regfield - register field to be written to the ModRm. This is either a register specifier
|
|
|
|
// or an opcode extension. In either case, the instruction determines the value for us.
|
|
|
|
//
|
2010-07-05 19:15:19 +00:00
|
|
|
void EmitSibMagic( uint regfield, const xIndirectVoid& info )
|
2009-11-06 21:45:30 +00:00
|
|
|
{
|
2016-01-16 19:47:13 +00:00
|
|
|
// 3 bits also on x86_64 (so max is 8)
|
|
|
|
// We might need to mask it on x86_64
|
2009-11-06 21:45:30 +00:00
|
|
|
pxAssertDev( regfield < 8, "Invalid x86 register identifier." );
|
|
|
|
int displacement_size = (info.Displacement == 0) ? 0 :
|
|
|
|
( ( info.IsByteSizeDisp() ) ? 1 : 2 );
|
2009-04-14 01:26:57 +00:00
|
|
|
|
2010-08-16 15:57:01 +00:00
|
|
|
pxAssert(!info.Base.IsEmpty() || !info.Index.IsEmpty() || displacement_size == 2);
|
2010-07-06 20:05:21 +00:00
|
|
|
|
2009-11-06 21:45:30 +00:00
|
|
|
if( !NeedsSibMagic( info ) )
|
|
|
|
{
|
|
|
|
// Use ModRm-only encoding, with the rm field holding an index/base register, if
|
|
|
|
// one has been specified. If neither register is specified then use Disp32 form,
|
|
|
|
// which is encoded as "EBP w/o displacement" (which is why EBP must always be
|
|
|
|
// encoded *with* a displacement of 0, if it would otherwise not have one).
|
|
|
|
|
|
|
|
if( info.Index.IsEmpty() )
|
|
|
|
{
|
|
|
|
EmitSibMagic( regfield, (void*)info.Displacement );
|
|
|
|
return;
|
2009-04-14 01:26:57 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2009-11-06 21:45:30 +00:00
|
|
|
if( info.Index == ebp && displacement_size == 0 )
|
|
|
|
displacement_size = 1; // forces [ebp] to be encoded as [ebp+0]!
|
2009-04-14 01:26:57 +00:00
|
|
|
|
2009-11-06 21:45:30 +00:00
|
|
|
ModRM( displacement_size, regfield, info.Index.Id );
|
2009-04-14 01:26:57 +00:00
|
|
|
}
|
2009-11-06 21:45:30 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// In order to encode "just" index*scale (and no base), we have to encode
|
|
|
|
// it as a special [index*scale + displacement] form, which is done by
|
|
|
|
// specifying EBP as the base register and setting the displacement field
|
|
|
|
// to zero. (same as ModRm w/o SIB form above, basically, except the
|
|
|
|
// ModRm_UseDisp flag is specified in the SIB instead of the ModRM field).
|
2009-04-14 01:26:57 +00:00
|
|
|
|
2009-11-06 21:45:30 +00:00
|
|
|
if( info.Base.IsEmpty() )
|
2009-04-14 01:26:57 +00:00
|
|
|
{
|
2009-11-06 21:45:30 +00:00
|
|
|
ModRM( 0, regfield, ModRm_UseSib );
|
|
|
|
SibSB( info.Scale, info.Index.Id, ModRm_UseDisp32 );
|
|
|
|
xWrite<s32>( info.Displacement );
|
|
|
|
return;
|
2009-04-14 01:26:57 +00:00
|
|
|
}
|
2009-11-06 21:45:30 +00:00
|
|
|
else
|
|
|
|
{
|
|
|
|
if( info.Base == ebp && displacement_size == 0 )
|
|
|
|
displacement_size = 1; // forces [ebp] to be encoded as [ebp+0]!
|
2009-04-15 21:00:32 +00:00
|
|
|
|
2009-11-06 21:45:30 +00:00
|
|
|
ModRM( displacement_size, regfield, ModRm_UseSib );
|
|
|
|
SibSB( info.Scale, info.Index.Id, info.Base.Id );
|
|
|
|
}
|
|
|
|
}
|
2009-04-17 18:47:04 +00:00
|
|
|
|
2009-11-06 21:45:30 +00:00
|
|
|
if( displacement_size != 0 )
|
|
|
|
{
|
|
|
|
if( displacement_size == 1 )
|
|
|
|
xWrite<s8>( info.Displacement );
|
|
|
|
else
|
|
|
|
xWrite<s32>( info.Displacement );
|
|
|
|
}
|
|
|
|
}
|
2009-04-17 18:47:04 +00:00
|
|
|
|
2009-11-06 21:45:30 +00:00
|
|
|
// Writes a ModRM byte for "Direct" register access forms, which is used for all
|
|
|
|
// instructions taking a form of [reg,reg].
|
|
|
|
void EmitSibMagic( uint reg1, const xRegisterBase& reg2 )
|
|
|
|
{
|
|
|
|
xWrite8( (Mod_Direct << 6) | (reg1 << 3) | reg2.Id );
|
|
|
|
}
|
2009-04-17 18:47:04 +00:00
|
|
|
|
2009-11-06 21:45:30 +00:00
|
|
|
void EmitSibMagic( const xRegisterBase& reg1, const xRegisterBase& reg2 )
|
|
|
|
{
|
|
|
|
xWrite8( (Mod_Direct << 6) | (reg1.Id << 3) | reg2.Id );
|
|
|
|
}
|
2009-04-17 18:47:04 +00:00
|
|
|
|
2009-11-06 21:45:30 +00:00
|
|
|
void EmitSibMagic( const xRegisterBase& reg1, const void* src )
|
|
|
|
{
|
|
|
|
EmitSibMagic( reg1.Id, src );
|
|
|
|
}
|
2009-04-17 18:47:04 +00:00
|
|
|
|
2010-07-05 19:15:19 +00:00
|
|
|
void EmitSibMagic( const xRegisterBase& reg1, const xIndirectVoid& sib )
|
2009-11-06 21:45:30 +00:00
|
|
|
{
|
|
|
|
EmitSibMagic( reg1.Id, sib );
|
|
|
|
}
|
2009-04-17 18:47:04 +00:00
|
|
|
|
2016-01-16 19:47:13 +00:00
|
|
|
//////////////////////////////////////////////////////////////////////////////////////////
|
2016-01-17 11:06:08 +00:00
|
|
|
__emitinline static void EmitRex(bool w, bool r, bool x, bool b)
|
2016-01-16 19:47:13 +00:00
|
|
|
{
|
2016-01-17 11:06:08 +00:00
|
|
|
#ifdef __x86_64__
|
|
|
|
u8 rex = 0x40 | (w << 3) | (r << 2) | (x << 1) | b;
|
|
|
|
if (rex != 0x40)
|
|
|
|
xWrite8(rex);
|
|
|
|
#endif
|
2016-01-16 19:47:13 +00:00
|
|
|
}
|
|
|
|
|
2016-01-17 11:06:08 +00:00
|
|
|
void EmitRex( uint regfield, const void* address )
|
2016-01-16 19:47:13 +00:00
|
|
|
{
|
2016-01-17 11:06:08 +00:00
|
|
|
pxAssert(0);
|
|
|
|
bool w = false;
|
|
|
|
bool r = false;
|
|
|
|
bool x = false;
|
|
|
|
bool b = false;
|
|
|
|
EmitRex(w, r, x, b);
|
2016-01-16 19:47:13 +00:00
|
|
|
}
|
|
|
|
|
2016-01-17 11:06:08 +00:00
|
|
|
void EmitRex( uint regfield, const xIndirectVoid& info )
|
2016-01-16 19:47:13 +00:00
|
|
|
{
|
2016-01-17 11:06:08 +00:00
|
|
|
bool w = info.Base.IsWide();
|
|
|
|
bool r = false;
|
|
|
|
bool x = false;
|
|
|
|
bool b = info.IsExtended();
|
|
|
|
EmitRex(w, r, x, b);
|
2016-01-16 19:47:13 +00:00
|
|
|
}
|
|
|
|
|
2016-01-17 11:06:08 +00:00
|
|
|
void EmitRex( uint reg1, const xRegisterBase& reg2 )
|
2016-01-16 19:47:13 +00:00
|
|
|
{
|
2016-01-17 11:06:08 +00:00
|
|
|
bool w = reg2.IsWide();
|
|
|
|
bool r = false;
|
|
|
|
bool x = false;
|
|
|
|
bool b = reg2.IsExtended();
|
|
|
|
EmitRex(w, r, x, b);
|
2016-01-16 19:47:13 +00:00
|
|
|
}
|
|
|
|
|
2016-01-17 11:06:08 +00:00
|
|
|
void EmitRex( const xRegisterBase& reg1, const xRegisterBase& reg2 )
|
2016-01-16 19:47:13 +00:00
|
|
|
{
|
2016-01-17 11:06:08 +00:00
|
|
|
bool w = reg1.IsWide();
|
|
|
|
bool r = reg1.IsExtended();
|
|
|
|
bool x = false;
|
|
|
|
bool b = reg2.IsExtended();
|
|
|
|
EmitRex(w, r, x, b);
|
2016-01-16 19:47:13 +00:00
|
|
|
}
|
|
|
|
|
2016-01-17 11:06:08 +00:00
|
|
|
void EmitRex( const xRegisterBase& reg1, const void* src )
|
|
|
|
{
|
|
|
|
pxAssert(0); //see fixme
|
|
|
|
bool w = reg1.IsWide();
|
|
|
|
bool r = reg1.IsExtended();
|
|
|
|
bool x = false;
|
|
|
|
bool b = false; // FIXME src.IsExtended();
|
|
|
|
EmitRex(w, r, x, b);
|
|
|
|
}
|
|
|
|
|
|
|
|
void EmitRex( const xRegisterBase& reg1, const xIndirectVoid& sib )
|
|
|
|
{
|
2016-06-29 17:30:15 +00:00
|
|
|
bool w = reg1.IsWide();
|
|
|
|
bool r = reg1.IsExtended();
|
|
|
|
bool x = sib.Index.IsExtended();
|
|
|
|
bool b = sib.Base.IsExtended();
|
2016-01-17 11:06:08 +00:00
|
|
|
EmitRex(w, r, x, b);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2009-11-06 21:45:30 +00:00
|
|
|
// --------------------------------------------------------------------------------------
|
|
|
|
// xSetPtr / xAlignPtr / xGetPtr / xAdvancePtr
|
|
|
|
// --------------------------------------------------------------------------------------
|
2009-04-17 18:47:04 +00:00
|
|
|
|
2009-04-15 15:45:52 +00:00
|
|
|
// Assigns the current emitter buffer target address.
|
|
|
|
// This is provided instead of using x86Ptr directly, since we may in the future find
|
|
|
|
// a need to change the storage class system for the x86Ptr 'under the hood.'
|
2009-07-03 20:12:33 +00:00
|
|
|
__emitinline void xSetPtr( void* ptr )
|
2009-04-15 15:45:52 +00:00
|
|
|
{
|
|
|
|
x86Ptr = (u8*)ptr;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Retrieves the current emitter buffer target address.
|
|
|
|
// This is provided instead of using x86Ptr directly, since we may in the future find
|
|
|
|
// a need to change the storage class system for the x86Ptr 'under the hood.'
|
2009-04-24 11:25:10 +00:00
|
|
|
__emitinline u8* xGetPtr()
|
2009-04-15 15:45:52 +00:00
|
|
|
{
|
|
|
|
return x86Ptr;
|
|
|
|
}
|
|
|
|
|
2009-07-03 20:12:33 +00:00
|
|
|
__emitinline void xAlignPtr( uint bytes )
|
2009-04-15 15:45:52 +00:00
|
|
|
{
|
|
|
|
// forward align
|
|
|
|
x86Ptr = (u8*)( ( (uptr)x86Ptr + bytes - 1) & ~(bytes - 1) );
|
|
|
|
}
|
|
|
|
|
2009-12-15 20:46:30 +00:00
|
|
|
// Performs best-case alignment for the target CPU, for use prior to starting a new
|
|
|
|
// function. This is not meant to be used prior to jump targets, since it doesn't
|
|
|
|
// add padding (additionally, speed benefit from jump alignment is minimal, and often
|
|
|
|
// a loss).
|
|
|
|
__emitinline void xAlignCallTarget()
|
|
|
|
{
|
|
|
|
// Core2/i7 CPUs prefer unaligned addresses. Checking for SSSE3 is a decent filter.
|
|
|
|
// (also align in debug modes for disasm convenience)
|
2010-04-25 00:31:27 +00:00
|
|
|
|
2009-12-15 20:46:30 +00:00
|
|
|
if( IsDebugBuild || !x86caps.hasSupplementalStreamingSIMD3Extensions )
|
|
|
|
{
|
|
|
|
// - P4's and earlier prefer 16 byte alignment.
|
|
|
|
// - AMD Athlons and Phenoms prefer 8 byte alignment, but I don't have an easy
|
|
|
|
// heuristic for it yet.
|
|
|
|
// - AMD Phenom IIs are unknown (either prefer 8 byte, or unaligned).
|
|
|
|
|
|
|
|
xAlignPtr( 16 );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
__emitinline u8* xGetAlignedCallTarget()
|
|
|
|
{
|
|
|
|
xAlignCallTarget();
|
|
|
|
return x86Ptr;
|
|
|
|
}
|
|
|
|
|
2009-04-24 11:25:10 +00:00
|
|
|
__emitinline void xAdvancePtr( uint bytes )
|
2009-04-15 15:45:52 +00:00
|
|
|
{
|
|
|
|
if( IsDevBuild )
|
|
|
|
{
|
|
|
|
// common debugger courtesy: advance with INT3 as filler.
|
|
|
|
for( uint i=0; i<bytes; i++ )
|
2009-04-24 11:25:10 +00:00
|
|
|
xWrite8( 0xcc );
|
2009-04-15 15:45:52 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
x86Ptr += bytes;
|
|
|
|
}
|
|
|
|
|
2009-11-06 21:45:30 +00:00
|
|
|
// --------------------------------------------------------------------------------------
|
2010-07-05 19:15:19 +00:00
|
|
|
// xAddressReg (operator overloads)
|
2009-11-06 21:45:30 +00:00
|
|
|
// --------------------------------------------------------------------------------------
|
2010-07-05 19:15:19 +00:00
|
|
|
xAddressVoid xAddressReg::operator+( const xAddressReg& right ) const
|
|
|
|
{
|
|
|
|
pxAssertMsg( right.Id != -1 || Id != -1, "Uninitialized x86 register." );
|
|
|
|
return xAddressVoid( *this, right );
|
|
|
|
}
|
|
|
|
|
|
|
|
xAddressVoid xAddressReg::operator+( s32 right ) const
|
|
|
|
{
|
|
|
|
pxAssertMsg( Id != -1, "Uninitialized x86 register." );
|
|
|
|
return xAddressVoid( *this, right );
|
|
|
|
}
|
|
|
|
|
|
|
|
xAddressVoid xAddressReg::operator+( const void* right ) const
|
|
|
|
{
|
|
|
|
pxAssertMsg( Id != -1, "Uninitialized x86 register." );
|
|
|
|
return xAddressVoid( *this, (sptr)right );
|
|
|
|
}
|
|
|
|
|
|
|
|
xAddressVoid xAddressReg::operator-( s32 right ) const
|
|
|
|
{
|
|
|
|
pxAssertMsg( Id != -1, "Uninitialized x86 register." );
|
|
|
|
return xAddressVoid( *this, -right );
|
|
|
|
}
|
|
|
|
|
|
|
|
xAddressVoid xAddressReg::operator-( const void* right ) const
|
|
|
|
{
|
|
|
|
pxAssertMsg( Id != -1, "Uninitialized x86 register." );
|
2014-07-30 23:18:10 +00:00
|
|
|
return xAddressVoid( *this, -(sptr)right );
|
2010-07-05 19:15:19 +00:00
|
|
|
}
|
|
|
|
|
2016-01-16 19:20:27 +00:00
|
|
|
xAddressVoid xAddressReg::operator*( int factor ) const
|
2010-07-05 19:15:19 +00:00
|
|
|
{
|
|
|
|
pxAssertMsg( Id != -1, "Uninitialized x86 register." );
|
|
|
|
return xAddressVoid( xEmptyReg, *this, factor );
|
|
|
|
}
|
|
|
|
|
|
|
|
xAddressVoid xAddressReg::operator<<( u32 shift ) const
|
|
|
|
{
|
|
|
|
pxAssertMsg( Id != -1, "Uninitialized x86 register." );
|
|
|
|
return xAddressVoid( xEmptyReg, *this, 1<<shift );
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// --------------------------------------------------------------------------------------
|
|
|
|
// xAddressVoid (method implementations)
|
|
|
|
// --------------------------------------------------------------------------------------
|
|
|
|
|
|
|
|
xAddressVoid::xAddressVoid( const xAddressReg& base, const xAddressReg& index, int factor, s32 displacement )
|
|
|
|
{
|
2014-07-30 23:18:10 +00:00
|
|
|
Base = base;
|
|
|
|
Index = index;
|
|
|
|
Factor = factor;
|
|
|
|
Displacement = displacement;
|
2010-07-05 19:15:19 +00:00
|
|
|
|
|
|
|
pxAssertMsg( base.Id != xRegId_Invalid, "Uninitialized x86 register." );
|
|
|
|
pxAssertMsg( index.Id != xRegId_Invalid, "Uninitialized x86 register." );
|
|
|
|
}
|
|
|
|
|
2016-01-16 19:20:27 +00:00
|
|
|
xAddressVoid::xAddressVoid( const xAddressReg& index, s32 displacement )
|
2010-07-05 19:15:19 +00:00
|
|
|
{
|
2014-07-30 23:18:10 +00:00
|
|
|
Base = xEmptyReg;
|
|
|
|
Index = index;
|
|
|
|
Factor = 0;
|
|
|
|
Displacement = displacement;
|
2010-07-05 19:15:19 +00:00
|
|
|
|
|
|
|
pxAssertMsg( index.Id != xRegId_Invalid, "Uninitialized x86 register." );
|
|
|
|
}
|
|
|
|
|
|
|
|
xAddressVoid::xAddressVoid( s32 displacement )
|
|
|
|
{
|
2014-07-30 23:18:10 +00:00
|
|
|
Base = xEmptyReg;
|
|
|
|
Index = xEmptyReg;
|
|
|
|
Factor = 0;
|
|
|
|
Displacement = displacement;
|
2010-07-05 19:15:19 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
xAddressVoid::xAddressVoid( const void* displacement )
|
|
|
|
{
|
2014-07-30 23:18:10 +00:00
|
|
|
Base = xEmptyReg;
|
|
|
|
Index = xEmptyReg;
|
|
|
|
Factor = 0;
|
2016-01-16 19:20:27 +00:00
|
|
|
#ifdef __x86_64__
|
|
|
|
pxAssert(0);
|
|
|
|
//Displacement = (s32)displacement;
|
|
|
|
#else
|
|
|
|
Displacement = (s32)displacement;
|
|
|
|
#endif
|
2010-07-05 19:15:19 +00:00
|
|
|
}
|
2009-11-06 21:45:30 +00:00
|
|
|
|
2010-07-05 19:15:19 +00:00
|
|
|
xAddressVoid& xAddressVoid::Add( const xAddressReg& src )
|
2009-11-06 21:45:30 +00:00
|
|
|
{
|
|
|
|
if( src == Index )
|
|
|
|
{
|
|
|
|
Factor++;
|
|
|
|
}
|
|
|
|
else if( src == Base )
|
|
|
|
{
|
|
|
|
// Compound the existing register reference into the Index/Scale pair.
|
|
|
|
Base = xEmptyReg;
|
|
|
|
|
|
|
|
if( src == Index )
|
|
|
|
Factor++;
|
|
|
|
else
|
|
|
|
{
|
|
|
|
pxAssertDev( Index.IsEmpty(), "x86Emitter: Only one scaled index register is allowed in an address modifier." );
|
|
|
|
Index = src;
|
|
|
|
Factor = 2;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if( Base.IsEmpty() )
|
|
|
|
Base = src;
|
|
|
|
else if( Index.IsEmpty() )
|
|
|
|
Index = src;
|
|
|
|
else
|
2010-08-16 15:01:13 +00:00
|
|
|
pxAssumeDev( false, L"x86Emitter: address modifiers cannot have more than two index registers." ); // oops, only 2 regs allowed per ModRm!
|
2009-11-06 21:45:30 +00:00
|
|
|
|
|
|
|
return *this;
|
|
|
|
}
|
|
|
|
|
2010-07-05 19:15:19 +00:00
|
|
|
xAddressVoid& xAddressVoid::Add( const xAddressVoid& src )
|
2009-11-06 21:45:30 +00:00
|
|
|
{
|
|
|
|
Add( src.Base );
|
|
|
|
Add( src.Displacement );
|
|
|
|
|
|
|
|
// If the factor is 1, we can just treat index like a base register also.
|
|
|
|
if( src.Factor == 1 )
|
|
|
|
{
|
|
|
|
Add( src.Index );
|
|
|
|
}
|
|
|
|
else if( Index.IsEmpty() )
|
|
|
|
{
|
|
|
|
Index = src.Index;
|
|
|
|
Factor = src.Factor;
|
|
|
|
}
|
|
|
|
else if( Index == src.Index )
|
|
|
|
{
|
|
|
|
Factor += src.Factor;
|
|
|
|
}
|
|
|
|
else
|
2010-08-16 15:01:13 +00:00
|
|
|
pxAssumeDev( false, L"x86Emitter: address modifiers cannot have more than two index registers." ); // oops, only 2 regs allowed per ModRm!
|
2009-11-06 21:45:30 +00:00
|
|
|
|
|
|
|
return *this;
|
|
|
|
}
|
|
|
|
|
2010-07-05 19:15:19 +00:00
|
|
|
xIndirectVoid::xIndirectVoid( const xAddressVoid& src )
|
2010-06-05 04:07:58 +00:00
|
|
|
{
|
|
|
|
Base = src.Base;
|
|
|
|
Index = src.Index;
|
|
|
|
Scale = src.Factor;
|
|
|
|
Displacement= src.Displacement;
|
|
|
|
|
|
|
|
Reduce();
|
|
|
|
}
|
|
|
|
|
2010-07-05 19:15:19 +00:00
|
|
|
xIndirectVoid::xIndirectVoid( s32 disp )
|
2010-06-05 04:07:58 +00:00
|
|
|
{
|
|
|
|
Base = xEmptyReg;
|
|
|
|
Index = xEmptyReg;
|
|
|
|
Scale = 0;
|
|
|
|
Displacement= disp;
|
|
|
|
|
|
|
|
// no reduction necessary :D
|
|
|
|
}
|
|
|
|
|
2010-07-05 19:15:19 +00:00
|
|
|
xIndirectVoid::xIndirectVoid( xAddressReg base, xAddressReg index, int scale, s32 displacement )
|
2010-06-05 04:07:58 +00:00
|
|
|
{
|
|
|
|
Base = base;
|
|
|
|
Index = index;
|
|
|
|
Scale = scale;
|
|
|
|
Displacement= displacement;
|
|
|
|
|
|
|
|
Reduce();
|
|
|
|
}
|
2009-11-06 21:45:30 +00:00
|
|
|
|
2009-04-19 05:24:20 +00:00
|
|
|
// Generates a 'reduced' ModSib form, which has valid Base, Index, and Scale values.
|
|
|
|
// Necessary because by default ModSib compounds registers into Index when possible.
|
|
|
|
//
|
|
|
|
// If the ModSib is in illegal form ([Base + Index*5] for example) then an assertion
|
2009-07-03 20:12:33 +00:00
|
|
|
// followed by an InvalidParameter Exception will be tossed around in haphazard
|
2009-04-19 05:24:20 +00:00
|
|
|
// fashion.
|
|
|
|
//
|
|
|
|
// Optimization Note: Currently VC does a piss poor job of inlining this, even though
|
|
|
|
// constant propagation *should* resove it to little or no code (VC's constprop fails
|
|
|
|
// on C++ class initializers). There is a work around [using array initializers instead]
|
2009-07-03 20:12:33 +00:00
|
|
|
// but it's too much trouble for code that isn't performance critical anyway.
|
2009-04-19 05:24:20 +00:00
|
|
|
// And, with luck, maybe VC10 will optimize it better and make it a non-issue. :D
|
|
|
|
//
|
2010-07-05 19:15:19 +00:00
|
|
|
void xIndirectVoid::Reduce()
|
2009-04-19 05:24:20 +00:00
|
|
|
{
|
|
|
|
if( Index.IsStackPointer() )
|
|
|
|
{
|
|
|
|
// esp cannot be encoded as the index, so move it to the Base, if possible.
|
|
|
|
// note: intentionally leave index assigned to esp also (generates correct
|
|
|
|
// encoding later, since ESP cannot be encoded 'alone')
|
|
|
|
|
2009-10-04 15:34:40 +00:00
|
|
|
pxAssert( Scale == 0 ); // esp can't have an index modifier!
|
|
|
|
pxAssert( Base.IsEmpty() ); // base must be empty or else!
|
2009-04-19 05:24:20 +00:00
|
|
|
|
|
|
|
Base = Index;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// If no index reg, then load the base register into the index slot.
|
|
|
|
if( Index.IsEmpty() )
|
|
|
|
{
|
|
|
|
Index = Base;
|
|
|
|
Scale = 0;
|
|
|
|
if( !Base.IsStackPointer() ) // prevent ESP from being encoded 'alone'
|
2009-11-06 21:45:30 +00:00
|
|
|
Base = xEmptyReg;
|
2009-04-19 05:24:20 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// The Scale has a series of valid forms, all shown here:
|
2009-07-03 20:12:33 +00:00
|
|
|
|
2009-04-19 05:24:20 +00:00
|
|
|
switch( Scale )
|
|
|
|
{
|
|
|
|
case 0: break;
|
|
|
|
case 1: Scale = 0; break;
|
|
|
|
case 2: Scale = 1; break;
|
|
|
|
|
|
|
|
case 3: // becomes [reg*2+reg]
|
2009-10-04 15:34:40 +00:00
|
|
|
pxAssertDev( Base.IsEmpty(), "Cannot scale an Index register by 3 when Base is not empty!" );
|
2009-04-19 05:24:20 +00:00
|
|
|
Base = Index;
|
|
|
|
Scale = 1;
|
|
|
|
break;
|
2009-07-03 20:12:33 +00:00
|
|
|
|
2009-04-19 05:24:20 +00:00
|
|
|
case 4: Scale = 2; break;
|
|
|
|
|
|
|
|
case 5: // becomes [reg*4+reg]
|
2009-10-04 15:34:40 +00:00
|
|
|
pxAssertDev( Base.IsEmpty(), "Cannot scale an Index register by 5 when Base is not empty!" );
|
2009-04-19 05:24:20 +00:00
|
|
|
Base = Index;
|
|
|
|
Scale = 2;
|
|
|
|
break;
|
2009-07-03 20:12:33 +00:00
|
|
|
|
2009-04-19 05:24:20 +00:00
|
|
|
case 6: // invalid!
|
2010-08-16 15:01:13 +00:00
|
|
|
pxAssumeDev( false, "x86 asm cannot scale a register by 6." );
|
2009-04-19 05:24:20 +00:00
|
|
|
break;
|
2009-07-03 20:12:33 +00:00
|
|
|
|
2009-04-19 05:24:20 +00:00
|
|
|
case 7: // so invalid!
|
2010-08-16 15:01:13 +00:00
|
|
|
pxAssumeDev( false, "x86 asm cannot scale a register by 7." );
|
2009-04-19 05:24:20 +00:00
|
|
|
break;
|
2009-07-03 20:12:33 +00:00
|
|
|
|
2009-04-19 05:24:20 +00:00
|
|
|
case 8: Scale = 3; break;
|
|
|
|
case 9: // becomes [reg*8+reg]
|
2009-10-04 15:34:40 +00:00
|
|
|
pxAssertDev( Base.IsEmpty(), "Cannot scale an Index register by 9 when Base is not empty!" );
|
2009-04-19 05:24:20 +00:00
|
|
|
Base = Index;
|
|
|
|
Scale = 3;
|
|
|
|
break;
|
2016-01-16 19:20:27 +00:00
|
|
|
|
2010-08-16 15:01:13 +00:00
|
|
|
jNO_DEFAULT
|
2009-04-19 05:24:20 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2010-07-05 19:15:19 +00:00
|
|
|
uint xIndirectVoid::GetOperandSize() const
|
2010-06-05 04:07:58 +00:00
|
|
|
{
|
2010-08-16 15:01:13 +00:00
|
|
|
pxFailDev( "Invalid operation on xIndirectVoid" );
|
2010-06-05 04:07:58 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2010-07-05 19:15:19 +00:00
|
|
|
xIndirectVoid& xIndirectVoid::Add( s32 imm )
|
2010-06-05 04:07:58 +00:00
|
|
|
{
|
|
|
|
Displacement += imm;
|
|
|
|
return *this;
|
|
|
|
}
|
2009-04-19 05:24:20 +00:00
|
|
|
|
2009-04-14 01:26:57 +00:00
|
|
|
// ------------------------------------------------------------------------
|
|
|
|
// Internal implementation of EmitSibMagic which has been custom tailored
|
|
|
|
// to optimize special forms of the Lea instructions accordingly, such
|
|
|
|
// as when a LEA can be replaced with a "MOV reg,imm" or "MOV reg,reg".
|
|
|
|
//
|
2009-04-14 12:37:48 +00:00
|
|
|
// preserve_flags - set to ture to disable use of SHL on [Index*Base] forms
|
|
|
|
// of LEA, which alters flags states.
|
|
|
|
//
|
2010-07-05 19:15:19 +00:00
|
|
|
static void EmitLeaMagic( const xRegisterInt& to, const xIndirectVoid& src, bool preserve_flags )
|
2009-04-08 06:25:40 +00:00
|
|
|
{
|
2009-07-03 20:12:33 +00:00
|
|
|
int displacement_size = (src.Displacement == 0) ? 0 :
|
2009-04-08 06:25:40 +00:00
|
|
|
( ( src.IsByteSizeDisp() ) ? 1 : 2 );
|
|
|
|
|
|
|
|
// See EmitSibMagic for commenting on SIB encoding.
|
|
|
|
|
|
|
|
if( !NeedsSibMagic( src ) )
|
|
|
|
{
|
|
|
|
// LEA Land: means we have either 1-register encoding or just an offset.
|
|
|
|
// offset is encodable as an immediate MOV, and a register is encodable
|
|
|
|
// as a register MOV.
|
|
|
|
|
|
|
|
if( src.Index.IsEmpty() )
|
|
|
|
{
|
2009-04-20 03:10:05 +00:00
|
|
|
xMOV( to, src.Displacement );
|
2009-04-08 06:25:40 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
else if( displacement_size == 0 )
|
|
|
|
{
|
2009-11-06 21:45:30 +00:00
|
|
|
_xMovRtoR( to, src.Index );
|
2009-04-08 06:25:40 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2009-04-15 15:45:52 +00:00
|
|
|
if( !preserve_flags )
|
|
|
|
{
|
|
|
|
// encode as MOV and ADD combo. Make sure to use the immediate on the
|
|
|
|
// ADD since it can encode as an 8-bit sign-extended value.
|
2009-07-03 20:12:33 +00:00
|
|
|
|
2009-11-06 21:45:30 +00:00
|
|
|
_xMovRtoR( to, src.Index );
|
2009-04-20 03:10:05 +00:00
|
|
|
xADD( to, src.Displacement );
|
2009-04-15 15:45:52 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// note: no need to do ebp+0 check since we encode all 0 displacements as
|
|
|
|
// register assignments above (via MOV)
|
2009-04-08 06:25:40 +00:00
|
|
|
|
2009-04-24 11:25:10 +00:00
|
|
|
xWrite8( 0x8d );
|
2009-04-15 15:45:52 +00:00
|
|
|
ModRM( displacement_size, to.Id, src.Index.Id );
|
|
|
|
}
|
2009-04-08 06:25:40 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if( src.Base.IsEmpty() )
|
|
|
|
{
|
2009-04-14 12:37:48 +00:00
|
|
|
if( !preserve_flags && (displacement_size == 0) )
|
2009-04-08 06:25:40 +00:00
|
|
|
{
|
|
|
|
// Encode [Index*Scale] as a combination of Mov and Shl.
|
2009-04-14 01:26:57 +00:00
|
|
|
// This is more efficient because of the bloated LEA format which requires
|
2009-04-14 12:37:48 +00:00
|
|
|
// a 32 bit displacement, and the compact nature of the alternative.
|
2009-04-14 01:26:57 +00:00
|
|
|
//
|
|
|
|
// (this does not apply to older model P4s with the broken barrel shifter,
|
|
|
|
// but we currently aren't optimizing for that target anyway).
|
|
|
|
|
2009-11-06 21:45:30 +00:00
|
|
|
_xMovRtoR( to, src.Index );
|
2009-04-20 03:10:05 +00:00
|
|
|
xSHL( to, src.Scale );
|
2009-04-08 06:25:40 +00:00
|
|
|
return;
|
|
|
|
}
|
2009-04-24 11:25:10 +00:00
|
|
|
xWrite8( 0x8d );
|
2009-04-08 06:25:40 +00:00
|
|
|
ModRM( 0, to.Id, ModRm_UseSib );
|
|
|
|
SibSB( src.Scale, src.Index.Id, ModRm_UseDisp32 );
|
2009-04-24 11:25:10 +00:00
|
|
|
xWrite32( src.Displacement );
|
2009-04-14 01:26:57 +00:00
|
|
|
return;
|
2009-04-08 06:25:40 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2009-04-15 15:45:52 +00:00
|
|
|
if( src.Scale == 0 )
|
|
|
|
{
|
|
|
|
if( !preserve_flags )
|
|
|
|
{
|
|
|
|
if( src.Index == esp )
|
|
|
|
{
|
|
|
|
// ESP is not encodable as an index (ix86 ignores it), thus:
|
2009-11-06 21:45:30 +00:00
|
|
|
_xMovRtoR( to, src.Base ); // will do the trick!
|
2009-04-20 03:10:05 +00:00
|
|
|
if( src.Displacement ) xADD( to, src.Displacement );
|
2009-04-15 15:45:52 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
else if( src.Displacement == 0 )
|
|
|
|
{
|
2009-11-06 21:45:30 +00:00
|
|
|
_xMovRtoR( to, src.Base );
|
|
|
|
_g1_EmitOp( G1Type_ADD, to, src.Index );
|
2009-04-15 15:45:52 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if( (src.Index == esp) && (src.Displacement == 0) )
|
|
|
|
{
|
|
|
|
// special case handling of ESP as Index, which is replaceable with
|
|
|
|
// a single MOV even when preserve_flags is set! :D
|
2009-07-03 20:12:33 +00:00
|
|
|
|
2009-11-06 21:45:30 +00:00
|
|
|
_xMovRtoR( to, src.Base );
|
2009-04-15 15:45:52 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2009-04-08 06:25:40 +00:00
|
|
|
if( src.Base == ebp && displacement_size == 0 )
|
|
|
|
displacement_size = 1; // forces [ebp] to be encoded as [ebp+0]!
|
|
|
|
|
2009-04-24 11:25:10 +00:00
|
|
|
xWrite8( 0x8d );
|
2009-04-08 06:25:40 +00:00
|
|
|
ModRM( displacement_size, to.Id, ModRm_UseSib );
|
|
|
|
SibSB( src.Scale, src.Index.Id, src.Base.Id );
|
|
|
|
}
|
|
|
|
}
|
2009-04-14 01:26:57 +00:00
|
|
|
|
|
|
|
if( displacement_size != 0 )
|
2009-04-08 06:25:40 +00:00
|
|
|
{
|
2009-04-15 21:00:32 +00:00
|
|
|
if( displacement_size == 1 )
|
2009-04-20 03:10:05 +00:00
|
|
|
xWrite<s8>( src.Displacement );
|
2009-04-15 21:00:32 +00:00
|
|
|
else
|
2009-04-20 03:10:05 +00:00
|
|
|
xWrite<s32>( src.Displacement );
|
2009-04-08 06:25:40 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-01-16 22:31:00 +00:00
|
|
|
__emitinline void xLEA( xRegister64 to, const xIndirectVoid& src, bool preserve_flags )
|
2009-04-08 06:25:40 +00:00
|
|
|
{
|
2009-04-14 12:37:48 +00:00
|
|
|
EmitLeaMagic( to, src, preserve_flags );
|
2009-04-08 06:25:40 +00:00
|
|
|
}
|
|
|
|
|
2016-01-16 22:31:00 +00:00
|
|
|
__emitinline void xLEA( xRegister32 to, const xIndirectVoid& src, bool preserve_flags )
|
|
|
|
{
|
|
|
|
EmitLeaMagic( to, src, preserve_flags );
|
|
|
|
}
|
2009-04-08 06:25:40 +00:00
|
|
|
|
2010-07-05 19:15:19 +00:00
|
|
|
__emitinline void xLEA( xRegister16 to, const xIndirectVoid& src, bool preserve_flags )
|
2009-04-08 06:25:40 +00:00
|
|
|
{
|
2009-04-24 11:25:10 +00:00
|
|
|
xWrite8( 0x66 );
|
2009-04-14 12:37:48 +00:00
|
|
|
EmitLeaMagic( to, src, preserve_flags );
|
2009-04-08 06:25:40 +00:00
|
|
|
}
|
|
|
|
|
2009-11-06 21:45:30 +00:00
|
|
|
// =====================================================================================================
|
|
|
|
// TEST / INC / DEC
|
|
|
|
// =====================================================================================================
|
2016-01-16 22:04:44 +00:00
|
|
|
void xImpl_Test::operator()( const xRegisterInt& to, const xRegisterInt& from ) const
|
2009-11-06 21:45:30 +00:00
|
|
|
{
|
2016-01-16 22:04:44 +00:00
|
|
|
pxAssert( to.GetOperandSize() == from.GetOperandSize() );
|
2016-01-17 15:42:39 +00:00
|
|
|
xOpWrite( to.GetPrefix16(), to.Is8BitOp() ? 0x84 : 0x85, from, to );
|
2009-11-06 21:45:30 +00:00
|
|
|
}
|
|
|
|
|
2016-01-16 23:29:19 +00:00
|
|
|
void xImpl_Test::operator()( const xIndirect64orLess& dest, int imm ) const
|
2009-11-06 21:45:30 +00:00
|
|
|
{
|
2016-01-17 15:42:39 +00:00
|
|
|
xOpWrite( dest.GetPrefix16(), dest.Is8BitOp() ? 0xf6 : 0xf7, 0, dest );
|
2009-11-06 21:45:30 +00:00
|
|
|
dest.xWriteImm( imm );
|
|
|
|
}
|
|
|
|
|
|
|
|
void xImpl_Test::operator()( const xRegisterInt& to, int imm ) const
|
|
|
|
{
|
2016-01-17 15:42:39 +00:00
|
|
|
if( to.IsAccumulator() ) {
|
|
|
|
xOpAccWrite( to.GetPrefix16(), to.Is8BitOp() ? 0xa8 : 0xa9, 0, to );
|
|
|
|
} else {
|
|
|
|
xOpWrite( to.GetPrefix16(), to.Is8BitOp() ? 0xf6 : 0xf7, 0, to );
|
2009-11-06 21:45:30 +00:00
|
|
|
}
|
|
|
|
to.xWriteImm( imm );
|
|
|
|
}
|
|
|
|
|
2016-01-16 22:04:44 +00:00
|
|
|
void xImpl_BitScan::operator()( const xRegister16or32or64& to, const xRegister16or32or64& from ) const {
|
|
|
|
pxAssert( to->GetOperandSize() == from->GetOperandSize() );
|
|
|
|
xOpWrite0F( from->GetPrefix16(), Opcode, to, from );
|
|
|
|
}
|
|
|
|
void xImpl_BitScan::operator()( const xRegister16or32or64& to, const xIndirectVoid& sibsrc ) const
|
2009-11-06 21:45:30 +00:00
|
|
|
{
|
2016-01-16 22:04:44 +00:00
|
|
|
xOpWrite0F( to->GetPrefix16(), Opcode, to, sibsrc );
|
2009-11-06 21:45:30 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void xImpl_IncDec::operator()( const xRegisterInt& to ) const
|
|
|
|
{
|
|
|
|
if( to.Is8BitOp() )
|
|
|
|
{
|
2016-01-17 15:42:39 +00:00
|
|
|
u8 regfield = isDec ? 1 : 0;
|
|
|
|
xOpWrite( to.GetPrefix16(), 0xfe, regfield, to);
|
2009-11-06 21:45:30 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2016-01-17 11:07:03 +00:00
|
|
|
#ifdef __x86_64__
|
|
|
|
pxAssertMsg(0, "Single Byte INC/DEC aren't valid in 64 bits."
|
|
|
|
"You need to use the ModR/M form (FF/0 FF/1 opcodes)");
|
|
|
|
#endif
|
2009-11-06 21:45:30 +00:00
|
|
|
to.prefix16();
|
|
|
|
xWrite8( (isDec ? 0x48 : 0x40) | to.Id );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-01-16 23:29:19 +00:00
|
|
|
void xImpl_IncDec::operator()( const xIndirect64orLess& to ) const
|
2009-11-06 21:45:30 +00:00
|
|
|
{
|
|
|
|
to.prefix16();
|
|
|
|
xWrite8( to.Is8BitOp() ? 0xfe : 0xff );
|
|
|
|
EmitSibMagic( isDec ? 1 : 0, to );
|
|
|
|
}
|
|
|
|
|
2016-01-16 21:40:50 +00:00
|
|
|
void xImpl_DwordShift::operator()( const xRegister16or32or64& to, const xRegister16or32or64& from, const xRegisterCL& /* clreg */ ) const {
|
|
|
|
pxAssert( to->GetOperandSize() == from->GetOperandSize() );
|
|
|
|
xOpWrite0F( from->GetPrefix16(), OpcodeBase+1, to, from );
|
2009-11-06 21:45:30 +00:00
|
|
|
}
|
2016-01-16 21:40:50 +00:00
|
|
|
|
|
|
|
void xImpl_DwordShift::operator()( const xRegister16or32or64& to, const xRegister16or32or64& from, u8 shiftcnt ) const
|
2009-11-06 21:45:30 +00:00
|
|
|
{
|
2016-01-16 21:40:50 +00:00
|
|
|
pxAssert( to->GetOperandSize() == from->GetOperandSize() );
|
2009-11-06 21:45:30 +00:00
|
|
|
if( shiftcnt != 0 )
|
2016-01-16 21:40:50 +00:00
|
|
|
xOpWrite0F( from->GetPrefix16(), OpcodeBase, to, from, shiftcnt );
|
2009-11-06 21:45:30 +00:00
|
|
|
}
|
|
|
|
|
2016-01-16 21:40:50 +00:00
|
|
|
void xImpl_DwordShift::operator()( const xIndirectVoid& dest, const xRegister16or32or64& from, const xRegisterCL& /* clreg */ ) const
|
2009-11-06 21:45:30 +00:00
|
|
|
{
|
2016-01-16 21:40:50 +00:00
|
|
|
xOpWrite0F( from->GetPrefix16(), OpcodeBase + 1, from, dest );
|
2009-11-06 21:45:30 +00:00
|
|
|
}
|
|
|
|
|
2016-01-16 21:40:50 +00:00
|
|
|
void xImpl_DwordShift::operator()( const xIndirectVoid& dest, const xRegister16or32or64& from, u8 shiftcnt ) const
|
2009-11-06 21:45:30 +00:00
|
|
|
{
|
|
|
|
if( shiftcnt != 0 )
|
2016-01-16 21:40:50 +00:00
|
|
|
xOpWrite0F( from->GetPrefix16(), OpcodeBase, from, dest, shiftcnt );
|
2009-11-06 21:45:30 +00:00
|
|
|
}
|
|
|
|
|
2009-11-06 21:51:39 +00:00
|
|
|
const xImpl_Test xTEST = { };
|
2009-11-06 21:45:30 +00:00
|
|
|
|
2009-11-06 21:51:39 +00:00
|
|
|
const xImpl_BitScan xBSF = { 0xbc };
|
|
|
|
const xImpl_BitScan xBSR = { 0xbd };
|
2009-11-06 21:45:30 +00:00
|
|
|
|
2009-11-06 21:51:39 +00:00
|
|
|
const xImpl_IncDec xINC = { false };
|
|
|
|
const xImpl_IncDec xDEC = { true };
|
2009-11-06 21:45:30 +00:00
|
|
|
|
|
|
|
const xImpl_DwordShift xSHLD = { 0xa4 };
|
|
|
|
const xImpl_DwordShift xSHRD = { 0xac };
|
2009-04-16 01:34:09 +00:00
|
|
|
|
2009-04-08 06:25:40 +00:00
|
|
|
//////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
// Push / Pop Emitters
|
|
|
|
//
|
|
|
|
// Note: pushad/popad implementations are intentionally left out. The instructions are
|
|
|
|
// invalid in x64, and are super slow on x32. Use multiple Push/Pop instructions instead.
|
|
|
|
|
2010-07-05 19:15:19 +00:00
|
|
|
__emitinline void xPOP( const xIndirectVoid& from )
|
2009-04-08 06:25:40 +00:00
|
|
|
{
|
2009-04-24 11:25:10 +00:00
|
|
|
xWrite8( 0x8f );
|
2009-04-17 18:47:04 +00:00
|
|
|
EmitSibMagic( 0, from );
|
2009-04-08 06:25:40 +00:00
|
|
|
}
|
|
|
|
|
2010-07-05 19:15:19 +00:00
|
|
|
__emitinline void xPUSH( const xIndirectVoid& from )
|
2009-04-08 06:25:40 +00:00
|
|
|
{
|
2009-04-24 11:25:10 +00:00
|
|
|
xWrite8( 0xff );
|
2009-04-17 18:47:04 +00:00
|
|
|
EmitSibMagic( 6, from );
|
2009-04-08 06:25:40 +00:00
|
|
|
}
|
|
|
|
|
2016-01-16 22:25:30 +00:00
|
|
|
__fi void xPOP( xRegister32or64 from ) { xWrite8( 0x58 | from->Id ); }
|
2009-04-24 11:25:10 +00:00
|
|
|
|
2010-08-09 04:10:38 +00:00
|
|
|
__fi void xPUSH( u32 imm ) { xWrite8( 0x68 ); xWrite32( imm ); }
|
2016-01-16 22:25:30 +00:00
|
|
|
__fi void xPUSH( xRegister32or64 from ) { xWrite8( 0x50 | from->Id ); }
|
2009-04-24 11:25:10 +00:00
|
|
|
|
|
|
|
// pushes the EFLAGS register onto the stack
|
2010-08-09 04:10:38 +00:00
|
|
|
__fi void xPUSHFD() { xWrite8( 0x9C ); }
|
2009-04-24 11:25:10 +00:00
|
|
|
// pops the EFLAGS register from the stack
|
2010-08-09 04:10:38 +00:00
|
|
|
__fi void xPOPFD() { xWrite8( 0x9D ); }
|
2009-04-24 11:25:10 +00:00
|
|
|
|
|
|
|
|
2009-04-19 02:14:50 +00:00
|
|
|
//////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
//
|
2009-04-24 11:25:10 +00:00
|
|
|
|
2010-08-09 04:10:38 +00:00
|
|
|
__fi void xLEAVE() { xWrite8( 0xC9 ); }
|
|
|
|
__fi void xRET() { xWrite8( 0xC3 ); }
|
|
|
|
__fi void xCBW() { xWrite16( 0x9866 ); }
|
|
|
|
__fi void xCWD() { xWrite8( 0x98 ); }
|
|
|
|
__fi void xCDQ() { xWrite8( 0x99 ); }
|
|
|
|
__fi void xCWDE() { xWrite8( 0x98 ); }
|
2009-04-24 11:25:10 +00:00
|
|
|
|
2010-08-09 04:10:38 +00:00
|
|
|
__fi void xLAHF() { xWrite8( 0x9f ); }
|
|
|
|
__fi void xSAHF() { xWrite8( 0x9e ); }
|
2009-04-24 11:25:10 +00:00
|
|
|
|
2010-08-09 04:10:38 +00:00
|
|
|
__fi void xSTC() { xWrite8( 0xF9 ); }
|
|
|
|
__fi void xCLC() { xWrite8( 0xF8 ); }
|
2009-04-24 11:25:10 +00:00
|
|
|
|
|
|
|
// NOP 1-byte
|
2010-08-09 04:10:38 +00:00
|
|
|
__fi void xNOP() { xWrite8(0x90); }
|
2009-04-24 11:25:10 +00:00
|
|
|
|
2010-08-09 04:10:38 +00:00
|
|
|
__fi void xINT( u8 imm )
|
2010-03-15 14:15:40 +00:00
|
|
|
{
|
|
|
|
if (imm == 3)
|
|
|
|
xWrite8(0xcc);
|
|
|
|
else
|
|
|
|
{
|
|
|
|
xWrite8(0xcd);
|
|
|
|
xWrite8(imm);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2010-08-09 04:10:38 +00:00
|
|
|
__fi void xINTO() { xWrite8(0xce); }
|
2010-03-15 14:15:40 +00:00
|
|
|
|
2016-01-16 22:31:00 +00:00
|
|
|
__emitinline void xBSWAP( const xRegister32or64& to )
|
2009-04-19 02:14:50 +00:00
|
|
|
{
|
2009-04-24 11:25:10 +00:00
|
|
|
xWrite8( 0x0F );
|
2016-01-16 22:31:00 +00:00
|
|
|
xWrite8( 0xC8 | to->Id );
|
2009-04-19 02:14:50 +00:00
|
|
|
}
|
|
|
|
|
2010-07-05 01:12:38 +00:00
|
|
|
static __aligned16 u64 xmm_data[iREGCNT_XMM*2];
|
|
|
|
|
2009-07-03 00:49:40 +00:00
|
|
|
__emitinline void xStoreReg( const xRegisterSSE& src )
|
|
|
|
{
|
2010-07-05 01:12:38 +00:00
|
|
|
xMOVDQA( ptr[&xmm_data[src.Id*2]], src );
|
2009-07-03 00:49:40 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
__emitinline void xRestoreReg( const xRegisterSSE& dest )
|
|
|
|
{
|
2010-07-05 01:12:38 +00:00
|
|
|
xMOVDQA( dest, ptr[&xmm_data[dest.Id*2]] );
|
2009-07-03 00:49:40 +00:00
|
|
|
}
|
|
|
|
|
2015-12-02 18:06:52 +00:00
|
|
|
//////////////////////////////////////////////////////////////////////////////////////////
|
2015-12-03 19:15:52 +00:00
|
|
|
// Helper object to handle ABI frame
|
|
|
|
#ifdef __GNUC__
|
2015-12-02 18:06:52 +00:00
|
|
|
|
|
|
|
#ifdef __x86_64__
|
2015-12-03 19:15:52 +00:00
|
|
|
// GCC ensures/requires stack to be 16 bytes aligned (but when?)
|
|
|
|
#define ALIGN_STACK(v) xADD(rsp, v)
|
2015-12-02 18:06:52 +00:00
|
|
|
#else
|
2015-12-03 19:15:52 +00:00
|
|
|
// GCC ensures/requires stack to be 16 bytes aligned before the call
|
|
|
|
// Call will store 4 bytes. EDI/ESI/EBX will take another 12 bytes.
|
|
|
|
// EBP will take 4 bytes if m_base_frame is enabled
|
|
|
|
#define ALIGN_STACK(v) xADD(esp, v)
|
2015-12-02 18:06:52 +00:00
|
|
|
#endif
|
|
|
|
|
|
|
|
#else
|
|
|
|
|
2015-12-03 19:15:52 +00:00
|
|
|
#define ALIGN_STACK(v)
|
2015-12-02 18:06:52 +00:00
|
|
|
|
|
|
|
#endif
|
|
|
|
|
2015-12-03 19:15:52 +00:00
|
|
|
xScopedStackFrame::xScopedStackFrame(bool base_frame, bool save_base_pointer, int offset)
|
2015-12-02 18:06:52 +00:00
|
|
|
{
|
2015-12-03 19:15:52 +00:00
|
|
|
m_base_frame = base_frame;
|
|
|
|
m_save_base_pointer = save_base_pointer;
|
|
|
|
m_offset = offset;
|
2015-12-02 18:06:52 +00:00
|
|
|
|
|
|
|
#ifdef __x86_64__
|
|
|
|
|
2015-12-03 19:15:52 +00:00
|
|
|
m_offset += 8; // Call stores the return address (4 bytes)
|
2015-12-02 18:06:52 +00:00
|
|
|
|
2015-12-03 19:15:52 +00:00
|
|
|
// Note rbp can surely be optimized in 64 bits
|
|
|
|
if (m_base_frame) {
|
|
|
|
xPUSH( rbp );
|
|
|
|
xMOV( rbp, rsp );
|
|
|
|
m_offset += 8;
|
|
|
|
} else if (m_save_base_pointer) {
|
|
|
|
xPUSH( rbp );
|
|
|
|
m_offset += 8;
|
|
|
|
}
|
|
|
|
|
|
|
|
xPUSH( rbx );
|
|
|
|
xPUSH( r12 );
|
|
|
|
xPUSH( r13 );
|
|
|
|
xPUSH( r14 );
|
|
|
|
xPUSH( r15 );
|
|
|
|
m_offset += 40;
|
2015-12-02 18:06:52 +00:00
|
|
|
|
|
|
|
#else
|
|
|
|
|
2015-12-03 19:15:52 +00:00
|
|
|
m_offset += 4; // Call stores the return address (4 bytes)
|
|
|
|
|
2015-12-02 18:06:52 +00:00
|
|
|
// Create a new frame
|
|
|
|
if (m_base_frame) {
|
|
|
|
xPUSH( ebp );
|
|
|
|
xMOV( ebp, esp );
|
2015-12-03 19:15:52 +00:00
|
|
|
m_offset += 4;
|
|
|
|
} else if (m_save_base_pointer) {
|
|
|
|
xPUSH( ebp );
|
|
|
|
m_offset += 4;
|
2015-12-02 18:06:52 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Save the register context
|
|
|
|
xPUSH( edi );
|
|
|
|
xPUSH( esi );
|
|
|
|
xPUSH( ebx );
|
2015-12-03 19:15:52 +00:00
|
|
|
m_offset += 12;
|
2015-12-02 18:06:52 +00:00
|
|
|
|
|
|
|
#endif
|
|
|
|
|
2015-12-03 19:15:52 +00:00
|
|
|
ALIGN_STACK(-(16 - m_offset % 16));
|
2009-04-07 08:42:25 +00:00
|
|
|
}
|
2015-12-02 18:06:52 +00:00
|
|
|
|
|
|
|
xScopedStackFrame::~xScopedStackFrame()
|
|
|
|
{
|
2015-12-03 19:15:52 +00:00
|
|
|
ALIGN_STACK(16 - m_offset % 16);
|
|
|
|
|
2015-12-02 18:06:52 +00:00
|
|
|
#ifdef __x86_64__
|
|
|
|
|
2015-12-03 19:15:52 +00:00
|
|
|
// Restore the register context
|
|
|
|
xPOP( r15 );
|
|
|
|
xPOP( r14 );
|
|
|
|
xPOP( r13 );
|
|
|
|
xPOP( r12 );
|
|
|
|
xPOP( rbx );
|
|
|
|
|
|
|
|
// Destroy the frame
|
2015-12-02 18:06:52 +00:00
|
|
|
if (m_base_frame) {
|
2015-12-03 19:15:52 +00:00
|
|
|
xLEAVE();
|
|
|
|
} else if (m_save_base_pointer) {
|
|
|
|
xPOP( rbp );
|
2015-12-02 18:06:52 +00:00
|
|
|
}
|
2015-12-03 19:15:52 +00:00
|
|
|
|
|
|
|
#else
|
2015-12-02 18:06:52 +00:00
|
|
|
|
|
|
|
// Restore the register context
|
|
|
|
xPOP( ebx );
|
|
|
|
xPOP( esi );
|
|
|
|
xPOP( edi );
|
|
|
|
|
|
|
|
// Destroy the frame
|
|
|
|
if (m_base_frame) {
|
|
|
|
xLEAVE();
|
2015-12-03 19:15:52 +00:00
|
|
|
} else if (m_save_base_pointer) {
|
|
|
|
xPOP( ebp );
|
2015-12-02 18:06:52 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
} // End namespace x86Emitter
|