1199 lines
29 KiB
C++
1199 lines
29 KiB
C++
|
/**
|
||
|
* vim: set ts=4 :
|
||
|
* =============================================================================
|
||
|
* SourceMod BinTools Extension
|
||
|
* Copyright (C) 2004-2017 AlliedModders LLC. All rights reserved.
|
||
|
* =============================================================================
|
||
|
*
|
||
|
* This program is free software; you can redistribute it and/or modify it under
|
||
|
* the terms of the GNU General Public License, version 3.0, as published by the
|
||
|
* Free Software Foundation.
|
||
|
*
|
||
|
* This program 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.
|
||
|
*
|
||
|
* You should have received a copy of the GNU General Public License along with
|
||
|
* this program. If not, see <http://www.gnu.org/licenses/>.
|
||
|
*
|
||
|
* As a special exception, AlliedModders LLC gives you permission to link the
|
||
|
* code of this program (as well as its derivative works) to "Half-Life 2," the
|
||
|
* "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software
|
||
|
* by the Valve Corporation. You must obey the GNU General Public License in
|
||
|
* all respects for all other code used. Additionally, AlliedModders LLC grants
|
||
|
* this exception to all derivative works. AlliedModders LLC defines further
|
||
|
* exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007),
|
||
|
* or <http://www.sourcemod.net/license.php>.
|
||
|
*/
|
||
|
|
||
|
#include <sm_platform.h>
|
||
|
#include "extension.h"
|
||
|
#include <jit_helpers.h>
|
||
|
#include <x86_macros.h>
|
||
|
#include "x64_macros.h"
|
||
|
#include "jit_compile.h"
|
||
|
|
||
|
#if defined PLATFORM_WINDOWS
|
||
|
jit_uint32_t g_StackUsage = 32; // Shadow space
|
||
|
#else
|
||
|
jit_uint32_t g_StackUsage = 0;
|
||
|
#endif
|
||
|
jit_uint32_t g_StackAlign = 0;
|
||
|
jit_uint32_t g_RegDecoder = 0;
|
||
|
jit_uint32_t g_FloatRegDecoder = 0;
|
||
|
jit_uint32_t g_PODCount = 0;
|
||
|
jit_uint32_t g_FloatCount = 0;
|
||
|
jit_uint32_t g_ParamCount = 0;
|
||
|
jit_uint32_t g_StackOffset = 0;
|
||
|
jit_uint8_t g_ThisPtrReg;
|
||
|
|
||
|
const jit_uint8_t STACK_PARAM = 16;
|
||
|
const jit_uint8_t INVALID_REG = 255;
|
||
|
|
||
|
inline jit_uint8_t NextScratchReg()
|
||
|
{
|
||
|
switch (g_RegDecoder++ % 3)
|
||
|
{
|
||
|
case 0:
|
||
|
return kREG_RAX;
|
||
|
case 1:
|
||
|
return kREG_R10;
|
||
|
case 2:
|
||
|
return kREG_R11;
|
||
|
default:
|
||
|
return INVALID_REG;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
#ifdef PLATFORM_POSIX
|
||
|
const int MAX_CLASSES = 2;
|
||
|
const int INT_REG_MAX = 6;
|
||
|
inline jit_uint8_t NextPODReg(size_t size)
|
||
|
{
|
||
|
switch (g_PODCount++)
|
||
|
{
|
||
|
case 0:
|
||
|
return kREG_RDI;
|
||
|
case 1:
|
||
|
return kREG_RSI;
|
||
|
case 2:
|
||
|
return kREG_RDX;
|
||
|
case 3:
|
||
|
return kREG_RCX;
|
||
|
case 4:
|
||
|
return kREG_R8;
|
||
|
case 5:
|
||
|
if (size == 16)
|
||
|
{
|
||
|
g_PODCount--;
|
||
|
return STACK_PARAM;
|
||
|
}
|
||
|
return kREG_R9;
|
||
|
default:
|
||
|
return STACK_PARAM;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
const int FLOAT_REG_MAX = 8;
|
||
|
inline jit_uint8_t NextFloatReg(size_t size)
|
||
|
{
|
||
|
switch (g_FloatCount++)
|
||
|
{
|
||
|
case 0:
|
||
|
return kREG_XMM0;
|
||
|
case 1:
|
||
|
return kREG_XMM1;
|
||
|
case 2:
|
||
|
return kREG_XMM2;
|
||
|
case 3:
|
||
|
return kREG_XMM3;
|
||
|
case 4:
|
||
|
return kREG_XMM4;
|
||
|
case 5:
|
||
|
return kREG_XMM5;
|
||
|
case 6:
|
||
|
return kREG_XMM6;
|
||
|
case 7:
|
||
|
if (size == 16)
|
||
|
{
|
||
|
g_FloatCount--;
|
||
|
return STACK_PARAM;
|
||
|
}
|
||
|
return kREG_XMM7;
|
||
|
default:
|
||
|
return STACK_PARAM;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
inline jit_uint8_t NextScratchFloatReg()
|
||
|
{
|
||
|
switch (g_FloatRegDecoder++ % 8)
|
||
|
{
|
||
|
case 0:
|
||
|
return kREG_XMM8;
|
||
|
case 1:
|
||
|
return kREG_XMM9;
|
||
|
case 2:
|
||
|
return kREG_XMM10;
|
||
|
case 3:
|
||
|
return kREG_XMM11;
|
||
|
case 4:
|
||
|
return kREG_XMM12;
|
||
|
case 5:
|
||
|
return kREG_XMM13;
|
||
|
case 6:
|
||
|
return kREG_XMM14;
|
||
|
case 7:
|
||
|
return kREG_XMM15;
|
||
|
default:
|
||
|
return INVALID_REG;
|
||
|
}
|
||
|
}
|
||
|
#elif defined PLATFORM_WINDOWS
|
||
|
const int NUM_ARG_REGS = 4;
|
||
|
inline jit_uint8_t NextPODReg(size_t size)
|
||
|
{
|
||
|
switch (g_ParamCount++)
|
||
|
{
|
||
|
case 0:
|
||
|
return kREG_RCX;
|
||
|
case 1:
|
||
|
return kREG_RDX;
|
||
|
case 2:
|
||
|
return kREG_R8;
|
||
|
case 3:
|
||
|
return kREG_R9;
|
||
|
default:
|
||
|
return STACK_PARAM;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
inline jit_uint8_t NextFloatReg(size_t size)
|
||
|
{
|
||
|
switch (g_ParamCount++)
|
||
|
{
|
||
|
case 0:
|
||
|
return kREG_XMM0;
|
||
|
case 1:
|
||
|
return kREG_XMM1;
|
||
|
case 2:
|
||
|
return kREG_XMM2;
|
||
|
case 3:
|
||
|
return kREG_XMM3;
|
||
|
default:
|
||
|
return STACK_PARAM;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
inline jit_uint8_t NextScratchFloatReg()
|
||
|
{
|
||
|
switch (g_FloatRegDecoder++ % 2)
|
||
|
{
|
||
|
case 0:
|
||
|
return kREG_XMM4;
|
||
|
case 1:
|
||
|
return kREG_XMM5;
|
||
|
default:
|
||
|
return INVALID_REG;
|
||
|
}
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
/********************
|
||
|
* Assembly Opcodes *
|
||
|
********************/
|
||
|
inline void Write_Execution_Prologue(JitWriter *jit, bool is_void, bool has_params)
|
||
|
{
|
||
|
//push rbp
|
||
|
//mov rbp, rsp
|
||
|
//if !is_void
|
||
|
// push r14
|
||
|
// mov r14, rsi | Windows: mov r14, rdx ; 2nd param (retbuf)
|
||
|
//if has_params
|
||
|
// push rbx
|
||
|
// mov rbx, rdi | Windows: mov rbx, rcx ; 1st param (param stack)
|
||
|
//push r15
|
||
|
//mov r15, rsp
|
||
|
//and rsp, 0xFFFFFFF0
|
||
|
//sub rsp, <alignment>
|
||
|
X64_Push_Reg(jit, kREG_RBP);
|
||
|
X64_Mov_Reg_Rm(jit, kREG_RBP, kREG_RSP, MOD_REG);
|
||
|
if (!is_void)
|
||
|
{
|
||
|
X64_Push_Reg(jit, kREG_R14);
|
||
|
#ifdef PLATFORM_POSIX
|
||
|
X64_Mov_Reg_Rm(jit, kREG_R14, kREG_RSI, MOD_REG);
|
||
|
#elif defined PLATFORM_WINDOWS
|
||
|
X64_Mov_Reg_Rm(jit, kREG_R14, kREG_RDX, MOD_REG);
|
||
|
#endif
|
||
|
}
|
||
|
if (has_params)
|
||
|
{
|
||
|
X64_Push_Reg(jit, kREG_RBX);
|
||
|
#ifdef PLATFORM_POSIX
|
||
|
X64_Mov_Reg_Rm(jit, kREG_RBX, kREG_RDI, MOD_REG);
|
||
|
#elif defined PLATFORM_WINDOWS
|
||
|
X64_Mov_Reg_Rm(jit, kREG_RBX, kREG_RCX, MOD_REG);
|
||
|
#endif
|
||
|
}
|
||
|
X64_Push_Reg(jit, kREG_R15);
|
||
|
X64_Mov_Reg_Rm(jit, kREG_R15, kREG_RSP, MOD_REG);
|
||
|
X64_And_Rm_Imm8(jit, kREG_RSP, MOD_REG, -16);
|
||
|
|
||
|
if (!jit->outbase)
|
||
|
{
|
||
|
/* Alloc this instruction before knowing the real stack usage */
|
||
|
X64_Sub_Rm_Imm32(jit, kREG_RSP, 1337, MOD_REG);
|
||
|
} else {
|
||
|
if (g_StackAlign)
|
||
|
X64_Sub_Rm_Imm32(jit, kREG_RSP, g_StackAlign, MOD_REG);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
inline void Write_Function_Epilogue(JitWriter *jit, bool is_void, bool has_params)
|
||
|
{
|
||
|
//mov rsp, r15
|
||
|
//pop r15
|
||
|
//if has_params
|
||
|
// pop rbx
|
||
|
//if !is_void
|
||
|
// pop r14
|
||
|
//mov rsp, rbp
|
||
|
//pop rbp
|
||
|
//ret
|
||
|
X64_Mov_Reg_Rm(jit, kREG_RSP, kREG_R15, MOD_REG);
|
||
|
X64_Pop_Reg(jit, kREG_R15);
|
||
|
if (has_params)
|
||
|
X64_Pop_Reg(jit, kREG_RBX);
|
||
|
if (!is_void)
|
||
|
X64_Pop_Reg(jit, kREG_R14);
|
||
|
X64_Mov_Reg_Rm(jit, kREG_RSP, kREG_RBP, MOD_REG);
|
||
|
X64_Pop_Reg(jit, kREG_RBP);
|
||
|
X64_Return(jit);
|
||
|
}
|
||
|
|
||
|
inline jit_uint8_t Write_PushPOD(JitWriter *jit, const SourceHook::PassInfo *info, unsigned int offset)
|
||
|
{
|
||
|
bool needStack = false;
|
||
|
jit_uint8_t reg = NextPODReg(info->size);
|
||
|
jit_uint8_t reg2;
|
||
|
|
||
|
if (reg == STACK_PARAM)
|
||
|
{
|
||
|
reg = NextScratchReg();
|
||
|
needStack = true;
|
||
|
}
|
||
|
|
||
|
if (info->flags & PASSFLAG_BYVAL)
|
||
|
{
|
||
|
switch (info->size)
|
||
|
{
|
||
|
case 1:
|
||
|
{
|
||
|
//movzx reg, BYTE PTR [ebx+<offset>]
|
||
|
if (!offset)
|
||
|
X64_Movzx_Reg64_Rm8(jit, reg, kREG_RBX, MOD_MEM_REG);
|
||
|
else if (offset < SCHAR_MAX)
|
||
|
X64_Movzx_Reg64_Rm8_Disp8(jit, reg, kREG_RBX, (jit_int8_t)offset);
|
||
|
else
|
||
|
X64_Movzx_Reg64_Rm8_Disp32(jit, reg, kREG_RBX, offset);
|
||
|
|
||
|
break;
|
||
|
}
|
||
|
case 2:
|
||
|
{
|
||
|
//movzx reg, WORD PTR [ebx+<offset>]
|
||
|
if (!offset)
|
||
|
X64_Movzx_Reg64_Rm16(jit, reg, kREG_RBX, MOD_MEM_REG);
|
||
|
else if (offset < SCHAR_MAX)
|
||
|
X64_Movzx_Reg64_Rm16_Disp8(jit, reg, kREG_RBX, (jit_int8_t)offset);
|
||
|
else
|
||
|
X64_Movzx_Reg64_Rm16_Disp32(jit, reg, kREG_RBX, offset);
|
||
|
|
||
|
break;
|
||
|
}
|
||
|
case 4:
|
||
|
{
|
||
|
//mov reg, DWORD PTR [ebx+<offset>]
|
||
|
if (!offset)
|
||
|
X64_Mov_Reg32_Rm(jit, reg, kREG_RBX, MOD_MEM_REG);
|
||
|
else if (offset < SCHAR_MAX)
|
||
|
X64_Mov_Reg32_Rm_Disp8(jit, reg, kREG_EBX, (jit_int8_t)offset);
|
||
|
else
|
||
|
X64_Mov_Reg32_Rm_Disp32(jit, reg, kREG_RBX, offset);
|
||
|
|
||
|
break;
|
||
|
}
|
||
|
case 8:
|
||
|
{
|
||
|
//mov reg, DWORD PTR [ebx+<offset>]
|
||
|
if (!offset)
|
||
|
X64_Mov_Reg_Rm(jit, reg, kREG_RBX, MOD_MEM_REG);
|
||
|
else if (offset < SCHAR_MAX)
|
||
|
X64_Mov_Reg_Rm_Disp8(jit, reg, kREG_EBX, (jit_int8_t)offset);
|
||
|
else
|
||
|
X64_Mov_Reg_Rm_Disp32(jit, reg, kREG_RBX, offset);
|
||
|
|
||
|
break;
|
||
|
}
|
||
|
case 16:
|
||
|
{
|
||
|
//mov reg, DWORD PTR [ebx+<offset>]
|
||
|
//mov reg2, DWORD PTR [ebx+<offset>+8]
|
||
|
reg2 = needStack ? NextScratchReg() : NextPODReg(8);
|
||
|
|
||
|
if (!offset)
|
||
|
X64_Mov_Reg_Rm(jit, reg, kREG_RBX, MOD_MEM_REG);
|
||
|
else if (offset < SCHAR_MAX)
|
||
|
X64_Mov_Reg_Rm_Disp8(jit, reg, kREG_RBX, (jit_int8_t)offset);
|
||
|
else
|
||
|
X64_Mov_Reg_Rm_Disp32(jit, reg, kREG_RBX, offset);
|
||
|
|
||
|
if (offset+8 < SCHAR_MAX)
|
||
|
X64_Mov_Reg_Rm_Disp8(jit, reg2, kREG_RBX, (jit_int8_t)(offset+8));
|
||
|
else
|
||
|
X64_Mov_Reg_Rm_Disp32(jit, reg2, kREG_RBX, offset+8);
|
||
|
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
} else if (info->flags & PASSFLAG_BYREF) {
|
||
|
//lea reg, [ebx+<offset>]
|
||
|
if (!offset)
|
||
|
X64_Mov_Reg_Rm(jit, reg, kREG_RBX, MOD_REG);
|
||
|
else if (offset < SCHAR_MAX)
|
||
|
X64_Lea_DispRegImm8(jit, reg, kREG_RBX, (jit_int8_t)offset);
|
||
|
else
|
||
|
X64_Lea_DispRegImm32(jit, reg, kREG_RBX, offset);
|
||
|
}
|
||
|
|
||
|
if (needStack)
|
||
|
{
|
||
|
// Move register value onto stack
|
||
|
//if g_StackUsage == 0
|
||
|
// mov [rsp], reg
|
||
|
//else
|
||
|
// mov [rsp+<g_StackUsage>], reg
|
||
|
//if info->size == 16
|
||
|
// mov [rsp+<g_StackUsage>+8], reg2
|
||
|
if (g_StackUsage == 0)
|
||
|
X64_Mov_RmRSP_Reg(jit, reg);
|
||
|
else if (g_StackUsage < SCHAR_MAX)
|
||
|
X64_Mov_RmRSP_Disp8_Reg(jit, reg, (jit_int8_t)g_StackUsage);
|
||
|
else
|
||
|
X64_Mov_RmRSP_Disp32_Reg(jit, reg, g_StackUsage);
|
||
|
|
||
|
if (info->size == 16)
|
||
|
{
|
||
|
if (g_StackUsage + 8 < SCHAR_MAX)
|
||
|
X64_Mov_RmRSP_Disp8_Reg(jit, reg2, (jit_int8_t)g_StackUsage + 8);
|
||
|
else
|
||
|
X64_Mov_RmRSP_Disp32_Reg(jit, reg2, (jit_int8_t)g_StackUsage + 8);
|
||
|
g_StackUsage += 16;
|
||
|
} else {
|
||
|
g_StackUsage += 8;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return reg;
|
||
|
}
|
||
|
|
||
|
inline void Write_PushFloat(JitWriter *jit, const SourceHook::PassInfo *info, unsigned int offset, uint8_t *floatRegs)
|
||
|
{
|
||
|
bool needStack = false;
|
||
|
jit_uint8_t floatReg = NextFloatReg(info->size);
|
||
|
jit_uint8_t floatReg2;
|
||
|
|
||
|
if (floatReg == STACK_PARAM)
|
||
|
{
|
||
|
floatReg = NextScratchFloatReg();
|
||
|
needStack = true;
|
||
|
}
|
||
|
|
||
|
if (info->flags & PASSFLAG_BYVAL)
|
||
|
{
|
||
|
switch (info->size)
|
||
|
{
|
||
|
case 4:
|
||
|
{
|
||
|
//if offset % 16 == 0
|
||
|
// movaps floatReg, [ebx+<offset>]
|
||
|
//else
|
||
|
// movups floatReg, [ebx+<offset>]
|
||
|
if (!offset) {
|
||
|
X64_Movaps_Rm(jit, floatReg, kREG_EBX);
|
||
|
} else if (offset < SCHAR_MAX) {
|
||
|
if (offset % 16 == 0)
|
||
|
X64_Movaps_Rm_Disp8(jit, floatReg, kREG_EBX, (jit_int8_t)offset);
|
||
|
else
|
||
|
X64_Movups_Rm_Disp8(jit, floatReg, kREG_EBX, (jit_int8_t)offset);
|
||
|
} else {
|
||
|
if (offset % 16 == 0)
|
||
|
X64_Movaps_Rm_Disp32(jit, floatReg, kREG_EBX, offset);
|
||
|
else
|
||
|
X64_Movups_Rm_Disp32(jit, floatReg, kREG_EBX, offset);
|
||
|
}
|
||
|
break;
|
||
|
}
|
||
|
case 8:
|
||
|
{
|
||
|
//if offset % 16 == 0
|
||
|
// movapd floatReg, [ebx+<offset>]
|
||
|
//else
|
||
|
// movupd floatReg, [ebx+<offset>]
|
||
|
if (!offset) {
|
||
|
X64_Movapd_Rm(jit, floatReg, kREG_EBX);
|
||
|
} else if (offset < SCHAR_MAX) {
|
||
|
if (offset % 16 == 0)
|
||
|
X64_Movapd_Rm_Disp8(jit, floatReg, kREG_EBX, (jit_int8_t)offset);
|
||
|
else
|
||
|
X64_Movupd_Rm_Disp8(jit, floatReg, kREG_EBX, (jit_int8_t)offset);
|
||
|
} else {
|
||
|
if (offset % 16 == 0)
|
||
|
X64_Movapd_Rm_Disp32(jit, floatReg, kREG_EBX, offset);
|
||
|
else
|
||
|
X64_Movupd_Rm_Disp32(jit, floatReg, kREG_EBX, offset);
|
||
|
}
|
||
|
break;
|
||
|
}
|
||
|
case 16:
|
||
|
//if offset % 16 == 0
|
||
|
// movaps floatReg, [ebx+<offset>]
|
||
|
//else
|
||
|
// movads floatReg, [ebx+<offset>]
|
||
|
//if (offset+8) % 16 == 0
|
||
|
// movaps floatReg2, [ebx+<offset>+8]
|
||
|
//else
|
||
|
// movads floatReg2, [ebx+<offset>+8]
|
||
|
floatReg2 = needStack ? NextScratchFloatReg() : NextFloatReg(8);
|
||
|
|
||
|
if (!offset) {
|
||
|
X64_Movaps_Rm(jit, floatReg, kREG_EBX);
|
||
|
X64_Movups_Rm_Disp8(jit, floatReg2, kREG_EBX, offset+8);
|
||
|
} else if (offset < SCHAR_MAX) {
|
||
|
if (offset % 16 == 0)
|
||
|
X64_Movaps_Rm_Disp8(jit, floatReg, kREG_EBX, (jit_int8_t)offset);
|
||
|
else
|
||
|
X64_Movups_Rm_Disp8(jit, floatReg, kREG_EBX, (jit_int8_t)offset);
|
||
|
if ((offset + 8) % 16 == 0)
|
||
|
X64_Movaps_Rm_Disp8(jit, floatReg2, kREG_EBX, (jit_int8_t)offset+8);
|
||
|
else
|
||
|
X64_Movups_Rm_Disp8(jit, floatReg2, kREG_EBX, (jit_int8_t)offset+8);
|
||
|
} else {
|
||
|
if (offset % 16 == 0)
|
||
|
X64_Movaps_Rm_Disp32(jit, floatReg, kREG_EBX, offset);
|
||
|
else
|
||
|
X64_Movups_Rm_Disp32(jit, floatReg, kREG_EBX, offset);
|
||
|
if ((offset + 8) % 16 == 0)
|
||
|
X64_Movaps_Rm_Disp32(jit, floatReg2, kREG_EBX, offset+8);
|
||
|
else
|
||
|
X64_Movups_Rm_Disp32(jit, floatReg2, kREG_EBX, offset+8);
|
||
|
}
|
||
|
}
|
||
|
} else if (info->flags & PASSFLAG_BYREF) {
|
||
|
//lea reg, [ebx+<offset>]
|
||
|
Write_PushPOD(jit, info, offset);
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
if (needStack)
|
||
|
{
|
||
|
// Move register value onto stack
|
||
|
//if g_StackUsage == 0
|
||
|
// movaps [rsp], floatReg
|
||
|
//else
|
||
|
// movaps [rsp+<g_StackUsage>], floatReg
|
||
|
//if info->size == 16
|
||
|
// movaps [rsp+<g_StackUsage>+8], floatReg2
|
||
|
if (g_StackUsage == 0) {
|
||
|
X64_Movaps_Rm_Reg(jit, kREG_RSP, floatReg);
|
||
|
} else if (g_StackUsage < SCHAR_MAX) {
|
||
|
if (g_StackUsage % 16 == 0)
|
||
|
X64_Movaps_Rm_Disp8_Reg(jit, kREG_RSP, floatReg, (jit_int8_t)g_StackUsage);
|
||
|
else
|
||
|
X64_Movups_Rm_Disp8_Reg(jit, kREG_RSP, floatReg, (jit_int8_t)g_StackUsage);
|
||
|
} else {
|
||
|
if (g_StackUsage % 16 == 0)
|
||
|
X64_Movaps_Rm_Disp32_Reg(jit, kREG_RSP, floatReg, g_StackUsage);
|
||
|
else
|
||
|
X64_Movups_Rm_Disp32_Reg(jit, kREG_RSP, floatReg, g_StackUsage);
|
||
|
}
|
||
|
|
||
|
if (info->size == 16)
|
||
|
{
|
||
|
if (g_StackUsage + 8 < SCHAR_MAX) {
|
||
|
if ((g_StackUsage + 8) % 16 == 0)
|
||
|
X64_Movaps_Rm_Disp8_Reg(jit, kREG_RSP, floatReg2, (jit_int8_t)g_StackUsage+8);
|
||
|
else
|
||
|
X64_Movups_Rm_Disp8_Reg(jit, kREG_RSP, floatReg2, (jit_int8_t)g_StackUsage+8);
|
||
|
} else {
|
||
|
if ((g_StackUsage + 8) % 16 == 0)
|
||
|
X64_Movaps_Rm_Disp32_Reg(jit, kREG_RSP, floatReg, g_StackUsage+8);
|
||
|
else
|
||
|
X64_Movups_Rm_Disp32_Reg(jit, kREG_RSP, floatReg, g_StackUsage+8);
|
||
|
}
|
||
|
g_StackUsage += 16;
|
||
|
} else {
|
||
|
g_StackUsage += 8;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (floatRegs)
|
||
|
{
|
||
|
floatRegs[0] = floatReg;
|
||
|
floatRegs[1] = info->size == 16 ? floatReg2 : INVALID_REG;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
#ifdef PLATFORM_WINDOWS
|
||
|
inline uint8_t MapFloatToIntReg(uint8_t floatReg)
|
||
|
{
|
||
|
switch (floatReg)
|
||
|
{
|
||
|
case kREG_XMM0:
|
||
|
return kREG_RCX;
|
||
|
case kREG_XMM1:
|
||
|
return kREG_RDX;
|
||
|
case kREG_XMM2:
|
||
|
return kREG_R8;
|
||
|
case kREG_XMM3:
|
||
|
return kREG_R9;
|
||
|
default:
|
||
|
return INVALID_REG;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
inline void Write_VarArgFloatCopy(JitWriter *jit, const SourceHook::PassInfo *info, uint8_t *floatRegs)
|
||
|
{
|
||
|
if (!floatRegs || floatRegs[0] == STACK_PARAM)
|
||
|
return;
|
||
|
|
||
|
uint8_t intReg1 = MapFloatToIntReg(floatRegs[0]);
|
||
|
|
||
|
switch (info->size)
|
||
|
{
|
||
|
case 4:
|
||
|
//movd intReg1, floatReg[0]
|
||
|
X64_Movd_Reg32_Xmm(jit, intReg1, floatRegs[0]);
|
||
|
break;
|
||
|
case 8:
|
||
|
//movq intReg1, floatReg[0]
|
||
|
X64_Movq_Reg_Xmm(jit, intReg1, floatRegs[0]);
|
||
|
break;
|
||
|
case 16:
|
||
|
//movq intReg1, floatReg[0]
|
||
|
//movq intReg2, floatReg[1]
|
||
|
X64_Movq_Reg_Xmm(jit, intReg1, floatRegs[0]);
|
||
|
if (floatRegs[1] != STACK_PARAM)
|
||
|
{
|
||
|
int intReg2 = MapFloatToIntReg(floatRegs[1]);
|
||
|
X64_Movq_Reg_Xmm(jit, intReg2, floatRegs[1]);
|
||
|
}
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
#endif // PLATFORM_WINDOWS
|
||
|
|
||
|
|
||
|
enum class ObjectClass
|
||
|
{
|
||
|
None,
|
||
|
Integer,
|
||
|
Pointer, // Special case of Integer
|
||
|
SSE,
|
||
|
SSEUp,
|
||
|
X87, // TODO? Really only applies to long doubles which Source doesn't use
|
||
|
X87Up, // TODO?
|
||
|
X87Complex, // TODO?
|
||
|
Memory
|
||
|
};
|
||
|
|
||
|
inline ObjectClass ClassifyType(ObjectField field)
|
||
|
{
|
||
|
switch (field)
|
||
|
{
|
||
|
case ObjectField::Boolean:
|
||
|
case ObjectField::Int8:
|
||
|
case ObjectField::Int16:
|
||
|
case ObjectField::Int32:
|
||
|
case ObjectField::Int64:
|
||
|
case ObjectField::Pointer:
|
||
|
return ObjectClass::Integer;
|
||
|
case ObjectField::Float:
|
||
|
case ObjectField::Double:
|
||
|
return ObjectClass::SSE;
|
||
|
default:
|
||
|
return ObjectClass::None;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
inline size_t TypeSize(ObjectField field)
|
||
|
{
|
||
|
switch (field)
|
||
|
{
|
||
|
case ObjectField::Boolean:
|
||
|
case ObjectField::Int8:
|
||
|
return 1;
|
||
|
case ObjectField::Int16:
|
||
|
return 2;
|
||
|
case ObjectField::Int32:
|
||
|
case ObjectField::Float:
|
||
|
return 4;
|
||
|
case ObjectField::Int64:
|
||
|
case ObjectField::Pointer:
|
||
|
case ObjectField::Double:
|
||
|
return 8;
|
||
|
default:
|
||
|
assert(false);
|
||
|
return 0;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
inline ObjectClass MergeClasses(ObjectClass class1, ObjectClass class2)
|
||
|
{
|
||
|
if (class1 == class2)
|
||
|
return class1;
|
||
|
else if (class1 == ObjectClass::None)
|
||
|
return class2;
|
||
|
else if (class2 == ObjectClass::None)
|
||
|
return class1;
|
||
|
else if (class1 == ObjectClass::Integer || class2 == ObjectClass::Integer)
|
||
|
return ObjectClass::Integer;
|
||
|
|
||
|
return ObjectClass::SSE;
|
||
|
}
|
||
|
|
||
|
inline int ClassifyObject(const PassInfo *info, ObjectClass *classes)
|
||
|
{
|
||
|
ObjectField *fields = info->fields;
|
||
|
unsigned int numFields = info->numFields;
|
||
|
int numWords = 1;
|
||
|
|
||
|
if (info->size > 16 || info->flags & PASSFLAG_OUNALIGN)
|
||
|
classes[0] = ObjectClass::Memory;
|
||
|
else if (info->flags & (PASSFLAG_ODTOR|PASSFLAG_OCOPYCTOR))
|
||
|
classes[0] = ObjectClass::Pointer;
|
||
|
else if (info->size > 8)
|
||
|
classes[0] = ObjectClass::None;
|
||
|
else if (numFields == 1 || numFields == 2)
|
||
|
classes[0] = ClassifyType(fields[0]);
|
||
|
else
|
||
|
classes[0] = ObjectClass::Integer;
|
||
|
|
||
|
if (classes[0] == ObjectClass::None)
|
||
|
{
|
||
|
numWords = int((info->size + 7) / 8);
|
||
|
|
||
|
unsigned int j = 0;
|
||
|
for (int i = 0; i < numWords; i++)
|
||
|
{
|
||
|
classes[i] = ObjectClass::None;
|
||
|
size_t sizeSoFar = 0;
|
||
|
for (int k = 0; j < numFields; j++, k++)
|
||
|
{
|
||
|
size_t sz = TypeSize(fields[j]);
|
||
|
if (sizeSoFar + sz > 8)
|
||
|
break;
|
||
|
else
|
||
|
sizeSoFar += sz;
|
||
|
|
||
|
if (k == 0 && sizeSoFar == 8) {
|
||
|
classes[i] = ClassifyType(fields[j++]);
|
||
|
break;
|
||
|
} else if (j + 1 >= numFields) {
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
const ObjectField &field1 = fields[j];
|
||
|
const ObjectField &field2 = fields[j + 1];
|
||
|
|
||
|
classes[i] = MergeClasses(ClassifyType(field1), ClassifyType(field2));
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return numWords;
|
||
|
}
|
||
|
|
||
|
inline void Write_PushObject(JitWriter *jit, const SourceHook::PassInfo *info, unsigned int offset,
|
||
|
const PassInfo *smInfo)
|
||
|
{
|
||
|
if (info->flags & PASSFLAG_BYVAL)
|
||
|
{
|
||
|
#ifdef PLATFORM_POSIX
|
||
|
ObjectClass classes[MAX_CLASSES];
|
||
|
int numWords = ClassifyObject(smInfo, classes);
|
||
|
|
||
|
if (classes[0] == ObjectClass::Pointer)
|
||
|
goto push_byref;
|
||
|
|
||
|
int neededIntRegs = 0;
|
||
|
int neededFloatRegs = 0;
|
||
|
for (int i = 0; i < numWords; i++)
|
||
|
{
|
||
|
switch (classes[i])
|
||
|
{
|
||
|
case ObjectClass::Integer:
|
||
|
neededIntRegs++;
|
||
|
break;
|
||
|
case ObjectClass::SSE:
|
||
|
neededFloatRegs++;
|
||
|
break;
|
||
|
default:
|
||
|
assert(false);
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (neededIntRegs + g_PODCount > INT_REG_MAX ||
|
||
|
neededFloatRegs + g_FloatCount > FLOAT_REG_MAX)
|
||
|
classes[0] = ObjectClass::Memory;
|
||
|
|
||
|
if (classes[0] != ObjectClass::Memory)
|
||
|
{
|
||
|
size_t sizeLeft = info->size;
|
||
|
for (int i = 0; i < numWords; i++)
|
||
|
{
|
||
|
switch (classes[i])
|
||
|
{
|
||
|
case ObjectClass::Integer:
|
||
|
{
|
||
|
SourceHook::PassInfo podInfo;
|
||
|
podInfo.size = (sizeLeft > 8) ? 8 : sizeLeft;
|
||
|
podInfo.type = SourceHook::PassInfo::PassType_Basic;
|
||
|
podInfo.flags = SourceHook::PassInfo::PassFlag_ByVal;
|
||
|
Write_PushPOD(jit, &podInfo, offset + (i * 8));
|
||
|
break;
|
||
|
}
|
||
|
case ObjectClass::SSE:
|
||
|
{
|
||
|
SourceHook::PassInfo floatInfo;
|
||
|
floatInfo.size = (sizeLeft > 8) ? 8 : sizeLeft;
|
||
|
floatInfo.type = SourceHook::PassInfo::PassType_Float;
|
||
|
floatInfo.flags = SourceHook::PassInfo::PassFlag_ByVal;
|
||
|
Write_PushFloat(jit, &floatInfo, offset + (i * 8), nullptr);
|
||
|
break;
|
||
|
}
|
||
|
default:
|
||
|
assert(false);
|
||
|
break;
|
||
|
}
|
||
|
if (sizeLeft > 8)
|
||
|
sizeLeft -= 8;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return;
|
||
|
|
||
|
#elif defined PLATFORM_WINDOWS
|
||
|
if (info->size < 64 && (info->size & (info->size - 1)) == 0)
|
||
|
goto push_byref;
|
||
|
else {
|
||
|
SourceHook::PassInfo podInfo;
|
||
|
podInfo.size = info->size;
|
||
|
podInfo.type = SourceHook::PassInfo::PassType_Basic;
|
||
|
podInfo.flags = SourceHook::PassInfo::PassFlag_ByVal;
|
||
|
Write_PushPOD(jit, &podInfo, offset);
|
||
|
}
|
||
|
|
||
|
return;
|
||
|
#endif
|
||
|
|
||
|
jit_uint32_t qwords = info->size >> 3;
|
||
|
jit_uint32_t bytes = info->size & 0x7;
|
||
|
|
||
|
//sub rsp, <size>
|
||
|
//cld
|
||
|
//push rdi
|
||
|
//push rsi
|
||
|
//lea rdi, [rsp+16]
|
||
|
//lea rsi, [rbx+<offs>]
|
||
|
//if dwords
|
||
|
// mov rcx, <dwords>
|
||
|
// rep movsq
|
||
|
//if bytes
|
||
|
// mov rcx, <bytes>
|
||
|
// rep movsb
|
||
|
//pop rsi
|
||
|
//pop rdi
|
||
|
|
||
|
//if (info->size < SCHAR_MAX)
|
||
|
//{
|
||
|
// X64_Sub_Rm_Imm8(jit, kREG_RSP, (jit_int8_t)info->size, MOD_REG);
|
||
|
//} else {
|
||
|
// X64_Sub_Rm_Imm32(jit, kREG_RSP, info->size, MOD_REG);
|
||
|
///}
|
||
|
X64_Cld(jit);
|
||
|
X64_Push_Reg(jit, kREG_RDI);
|
||
|
X64_Push_Reg(jit, kREG_RSI);
|
||
|
if (g_StackUsage + 16 < SCHAR_MAX)
|
||
|
X64_Lea_Reg_DispRegMultImm8(jit, kREG_RDI, kREG_NOIDX, kREG_RSP, NOSCALE, g_StackUsage+16);
|
||
|
else
|
||
|
X64_Lea_Reg_DispRegMultImm32(jit, kREG_RDI, kREG_NOIDX, kREG_RSP, NOSCALE, g_StackUsage+16);
|
||
|
|
||
|
if (!offset)
|
||
|
X64_Mov_Reg_Rm(jit, kREG_RSI, kREG_RBX, MOD_REG);
|
||
|
else if (offset < SCHAR_MAX)
|
||
|
X64_Lea_DispRegImm8(jit, kREG_RSI, kREG_RBX, (jit_int8_t)offset);
|
||
|
else
|
||
|
X64_Lea_DispRegImm32(jit, kREG_RSI, kREG_RBX, offset);
|
||
|
|
||
|
if (qwords)
|
||
|
{
|
||
|
X64_Mov_Reg_Imm32(jit, kREG_RCX, qwords);
|
||
|
X64_Rep(jit);
|
||
|
X64_Movsq(jit);
|
||
|
}
|
||
|
if (bytes)
|
||
|
{
|
||
|
X64_Mov_Reg_Imm32(jit, kREG_RCX, bytes);
|
||
|
X64_Rep(jit);
|
||
|
X64_Movsb(jit);
|
||
|
}
|
||
|
X64_Pop_Reg(jit, kREG_RSI);
|
||
|
X64_Pop_Reg(jit, kREG_RDI);
|
||
|
|
||
|
g_StackUsage += info->size;
|
||
|
} else if (info->flags & PASSFLAG_BYREF) {
|
||
|
push_byref:
|
||
|
//lea reg, [ebx+<offset>]
|
||
|
SourceHook::PassInfo podInfo;
|
||
|
podInfo.size = sizeof(void *);
|
||
|
podInfo.type = SourceHook::PassInfo::PassType_Basic;
|
||
|
podInfo.flags = SourceHook::PassInfo::PassFlag_ByRef;
|
||
|
Write_PushPOD(jit, &podInfo, offset);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
inline void Write_PushThisPtr(JitWriter *jit)
|
||
|
{
|
||
|
SourceHook::PassInfo podInfo;
|
||
|
podInfo.size = 8;
|
||
|
podInfo.type = SourceHook::PassInfo::PassType_Basic;
|
||
|
podInfo.flags = SourceHook::PassInfo::PassFlag_ByVal;
|
||
|
g_ThisPtrReg = Write_PushPOD(jit, &podInfo, 0);
|
||
|
}
|
||
|
|
||
|
inline void Write_PushRetBuffer(JitWriter *jit)
|
||
|
{
|
||
|
jit_uint8_t reg = NextPODReg(8);
|
||
|
|
||
|
//mov reg, r14
|
||
|
X64_Mov_Reg_Rm(jit, reg, kREG_R14, MOD_REG);
|
||
|
}
|
||
|
|
||
|
inline void Write_CallFunction(JitWriter *jit, FuncAddrMethod method, CallWrapper *pWrapper)
|
||
|
{
|
||
|
if (method == FuncAddr_Direct)
|
||
|
{
|
||
|
int64_t diff = (intptr_t)pWrapper->GetCalleeAddr() - ((intptr_t)jit->outbase + jit->get_outputpos() + 5);
|
||
|
int32_t upperBits = (diff >> 32);
|
||
|
if (upperBits == 0 || upperBits == -1) {
|
||
|
//call <addr>
|
||
|
jitoffs_t call = X64_Call_Imm32(jit, 0);
|
||
|
X64_Write_Jump32_Abs(jit, call, pWrapper->GetCalleeAddr());
|
||
|
} else {
|
||
|
//mov rax, <addr>
|
||
|
//call rax
|
||
|
X64_Mov_Reg_Imm64(jit, kREG_RAX, (jit_int64_t)pWrapper->GetCalleeAddr());
|
||
|
X64_Call_Reg(jit, kREG_RAX);
|
||
|
}
|
||
|
} else if (method == FuncAddr_VTable) {
|
||
|
//*(this + thisOffs + vtblOffs)[vtblIdx]
|
||
|
// mov r10, [g_ThisPtrReg+<thisOffs>+<vtblOffs>]
|
||
|
// mov r11, [r10+<vtablIdx>*8]
|
||
|
// call r11
|
||
|
SourceHook::MemFuncInfo *funcInfo = pWrapper->GetMemFuncInfo();
|
||
|
jit_uint32_t total_offs = funcInfo->thisptroffs + funcInfo->vtbloffs;
|
||
|
jit_uint32_t vfunc_pos = funcInfo->vtblindex * 8;
|
||
|
|
||
|
//X64_Mov_Reg_Rm(jit, kREG_RAX, kREG_RBX, MOD_MEM_REG);
|
||
|
if (total_offs < SCHAR_MAX)
|
||
|
{
|
||
|
X64_Mov_Reg_Rm_Disp8(jit, kREG_R10, g_ThisPtrReg, (jit_int8_t)total_offs);
|
||
|
} else if (!total_offs) {
|
||
|
X64_Mov_Reg_Rm(jit, kREG_R10, g_ThisPtrReg, MOD_MEM_REG);
|
||
|
} else {
|
||
|
X64_Mov_Reg_Rm_Disp32(jit, kREG_R10, g_ThisPtrReg, total_offs);
|
||
|
}
|
||
|
if (vfunc_pos < SCHAR_MAX)
|
||
|
{
|
||
|
X64_Mov_Reg_Rm_Disp8(jit, kREG_R11, kREG_R10, (jit_int8_t)vfunc_pos);
|
||
|
} else if (!vfunc_pos) {
|
||
|
X64_Mov_Reg_Rm(jit, kREG_R11, kREG_R10, MOD_MEM_REG);
|
||
|
} else {
|
||
|
X64_Mov_Reg_Rm_Disp32(jit, kREG_R11, kREG_R10, vfunc_pos);
|
||
|
}
|
||
|
X64_Call_Reg(jit, kREG_R11);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
inline void Write_VarArgFloatCount(JitWriter *jit)
|
||
|
{
|
||
|
//mov al, g_FloatCount
|
||
|
X64_Mov_Reg8_Imm8(jit, kREG_RAX, (jit_int8_t)g_FloatCount);
|
||
|
}
|
||
|
|
||
|
inline void Write_RectifyStack(JitWriter *jit, jit_uint32_t value)
|
||
|
{
|
||
|
//add rsp, <value>
|
||
|
if (value < SCHAR_MAX)
|
||
|
{
|
||
|
X64_Add_Rm_Imm8(jit, kREG_RSP, (jit_int8_t)value, MOD_REG);
|
||
|
} else {
|
||
|
X64_Add_Rm_Imm32(jit, kREG_RSP, value, MOD_REG);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
inline void Write_MovRet2Buf(JitWriter *jit, const PassInfo *pRet, ObjectClass *classes, int numWords)
|
||
|
{
|
||
|
if (pRet->type == PassType_Float)
|
||
|
{
|
||
|
switch (pRet->size)
|
||
|
{
|
||
|
case 4:
|
||
|
{
|
||
|
//movups xmmword ptr [r14], xmm0
|
||
|
X64_Movups_Rm_Reg(jit, kREG_R14, kREG_XMM0);
|
||
|
break;
|
||
|
}
|
||
|
case 8:
|
||
|
{
|
||
|
//movupd xmmword ptr [r14], xmm0
|
||
|
X64_Movupd_Rm_Reg(jit, kREG_R14, kREG_XMM0);
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
return;
|
||
|
}
|
||
|
else if (pRet->type == PassType_Basic)
|
||
|
{
|
||
|
switch (pRet->size)
|
||
|
{
|
||
|
case 1:
|
||
|
{
|
||
|
//mov BYTE PTR [r14], al
|
||
|
X64_Mov_Rm8_Reg8(jit, kREG_R14, kREG_RAX, MOD_MEM_REG);
|
||
|
break;
|
||
|
}
|
||
|
case 2:
|
||
|
{
|
||
|
//mov WORD PTR [r14], ax
|
||
|
X64_Mov_Rm16_Reg16(jit, kREG_R14, kREG_RAX, MOD_MEM_REG);
|
||
|
break;
|
||
|
}
|
||
|
case 4:
|
||
|
{
|
||
|
//mov DWORD PTR [r14], rax
|
||
|
X64_Mov_Rm32_Reg32(jit, kREG_R14, kREG_RAX, MOD_MEM_REG);
|
||
|
break;
|
||
|
}
|
||
|
case 8:
|
||
|
{
|
||
|
//mov QWORD PTR [r14], rax
|
||
|
X64_Mov_Rm_Reg(jit, kREG_R14, kREG_RAX, MOD_MEM_REG);
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
#ifdef PLATFORM_POSIX
|
||
|
else
|
||
|
{
|
||
|
// Return value registers
|
||
|
jit_uint8_t intRetReg = kREG_RAX;
|
||
|
jit_uint8_t floatRetReg = kREG_XMM0;
|
||
|
jit_int8_t offset = 0;
|
||
|
|
||
|
assert(numWords <= 2);
|
||
|
|
||
|
for (int i = 0; i < numWords; i++)
|
||
|
{
|
||
|
ObjectClass &cls = classes[i];
|
||
|
|
||
|
if (cls == ObjectClass::Integer)
|
||
|
{
|
||
|
//mov QWORD PTR [r14+offset], intRetReg ; rax or rdx
|
||
|
if (!offset)
|
||
|
X64_Mov_Rm_Reg(jit, kREG_R14, intRetReg, MOD_MEM_REG);
|
||
|
else
|
||
|
X64_Mov_Rm_Reg_Disp8(jit, kREG_R14, intRetReg, offset);
|
||
|
intRetReg = kREG_RDX;
|
||
|
}
|
||
|
else if (cls == ObjectClass::SSE)
|
||
|
{
|
||
|
//movupd xmmword ptr [r14+offset], floatRetReg ; xmm0 or xmm1
|
||
|
if (!offset)
|
||
|
X64_Movupd_Rm_Reg(jit, kREG_R14, floatRetReg);
|
||
|
else
|
||
|
X64_Movupd_Rm_Disp8_Reg(jit, kREG_R14, floatRetReg, offset);
|
||
|
floatRetReg = kREG_XMM1;
|
||
|
}
|
||
|
offset += 8;
|
||
|
}
|
||
|
}
|
||
|
#endif
|
||
|
}
|
||
|
|
||
|
/******************************
|
||
|
* Assembly Compiler Function *
|
||
|
******************************/
|
||
|
|
||
|
void *JIT_CallCompile(CallWrapper *pWrapper, FuncAddrMethod method)
|
||
|
{
|
||
|
JitWriter writer;
|
||
|
JitWriter *jit = &writer;
|
||
|
|
||
|
jit_uint32_t CodeSize = 0;
|
||
|
bool Needs_Retbuf = false;
|
||
|
CallConvention Convention = pWrapper->GetCallConvention();
|
||
|
jit_uint32_t ParamCount = pWrapper->GetParamCount();
|
||
|
const PassInfo *pRet = pWrapper->GetReturnInfo();
|
||
|
bool hasParams = (ParamCount || Convention == CallConv_ThisCall);
|
||
|
#ifdef PLATFORM_POSIX
|
||
|
ObjectClass classes[MAX_CLASSES];
|
||
|
int numWords;
|
||
|
#endif
|
||
|
|
||
|
g_StackUsage = 0;
|
||
|
|
||
|
writer.outbase = NULL;
|
||
|
writer.outptr = NULL;
|
||
|
|
||
|
jit_rewind:
|
||
|
/* Write function prologue */
|
||
|
Write_Execution_Prologue(jit, (pRet) ? false : true, hasParams);
|
||
|
|
||
|
#if defined PLATFORM_WINDOWS
|
||
|
/* This ptr is always first on Windows */
|
||
|
if (Convention == CallConv_ThisCall)
|
||
|
{
|
||
|
Write_PushThisPtr(jit);
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
/* Skip the return buffer stuff if this is a void function */
|
||
|
if (!pRet)
|
||
|
{
|
||
|
goto skip_retbuffer;
|
||
|
}
|
||
|
|
||
|
if ((pRet->type == PassType_Object) && (pRet->flags & PASSFLAG_BYVAL))
|
||
|
{
|
||
|
#ifdef PLATFORM_POSIX
|
||
|
numWords = ClassifyObject(pRet, classes);
|
||
|
|
||
|
if (classes[0] == ObjectClass::Memory || classes[0] == ObjectClass::Pointer)
|
||
|
Needs_Retbuf = true;
|
||
|
#elif defined PLATFORM_WINDOWS
|
||
|
if (pRet->size > 8 || (pRet->size & (pRet->size - 1)) != 0 ||
|
||
|
(pRet->flags & PASSFLAG_ODTOR|PASSFLAG_OCTOR|PASSFLAG_OASSIGNOP))
|
||
|
Needs_Retbuf = true;
|
||
|
#endif
|
||
|
}
|
||
|
|
||
|
/* Prepare the return buffer in case we are returning objects by value. */
|
||
|
if (Needs_Retbuf)
|
||
|
{
|
||
|
Write_PushRetBuffer(jit);
|
||
|
}
|
||
|
|
||
|
skip_retbuffer:
|
||
|
#if defined PLATFORM_POSIX
|
||
|
/* This ptr comes after retbuf ptr on Linux/macOS */
|
||
|
if (Convention == CallConv_ThisCall)
|
||
|
{
|
||
|
Write_PushThisPtr(jit);
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
/* Write parameter push code */
|
||
|
for (jit_uint32_t i = 0; i < ParamCount; i++)
|
||
|
{
|
||
|
unsigned int offset = pWrapper->GetParamOffset(i);
|
||
|
const SourceHook::PassInfo *info = pWrapper->GetSHParamInfo(i);
|
||
|
assert(info != NULL);
|
||
|
|
||
|
switch (info->type)
|
||
|
{
|
||
|
case SourceHook::PassInfo::PassType_Basic:
|
||
|
{
|
||
|
Write_PushPOD(jit, info, offset);
|
||
|
break;
|
||
|
}
|
||
|
case SourceHook::PassInfo::PassType_Float:
|
||
|
{
|
||
|
#ifdef PLATFORM_WINDOWS
|
||
|
if ((info->flags & PASSFLAG_BYVAL) && (pWrapper->GetFunctionFlags() & FNFLAG_VARARGS))
|
||
|
{
|
||
|
uint8_t floatRegs[2];
|
||
|
Write_PushFloat(jit, info, offset, floatRegs);
|
||
|
Write_VarArgFloatCopy(jit, info, floatRegs);
|
||
|
}
|
||
|
else
|
||
|
#endif
|
||
|
{
|
||
|
Write_PushFloat(jit, info, offset, nullptr);
|
||
|
}
|
||
|
break;
|
||
|
}
|
||
|
case SourceHook::PassInfo::PassType_Object:
|
||
|
{
|
||
|
const PassEncode *paramInfo = pWrapper->GetParamInfo(i);
|
||
|
Write_PushObject(jit, info, offset, ¶mInfo->info);
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/* Write the calling code */
|
||
|
Write_CallFunction(jit, method, pWrapper);
|
||
|
|
||
|
#ifdef PLATFORM_POSIX
|
||
|
if (pWrapper->GetFunctionFlags() & FNFLAG_VARARGS)
|
||
|
Write_VarArgFloatCount(jit);
|
||
|
#endif
|
||
|
|
||
|
/* Clean up the calling stack */
|
||
|
if (hasParams && g_StackUsage)
|
||
|
Write_RectifyStack(jit, g_StackAlign);
|
||
|
|
||
|
/* Copy the return type to the return buffer if the function is not void */
|
||
|
if (pRet && !Needs_Retbuf)
|
||
|
{
|
||
|
#ifdef PLATFORM_POSIX
|
||
|
Write_MovRet2Buf(jit, pRet, classes, numWords);
|
||
|
#elif defined PLATFORM_WINDOWS
|
||
|
Write_MovRet2Buf(jit, pRet, nullptr, 0);
|
||
|
#endif
|
||
|
}
|
||
|
|
||
|
/* Write Function Epilogue */
|
||
|
Write_Function_Epilogue(jit, (pRet) ? false : true, hasParams);
|
||
|
|
||
|
if (writer.outbase == NULL)
|
||
|
{
|
||
|
CodeSize = writer.get_outputpos();
|
||
|
writer.outbase = (jitcode_t)g_SPEngine->AllocatePageMemory(CodeSize);
|
||
|
g_SPEngine->SetReadWrite(writer.outbase);
|
||
|
writer.outptr = writer.outbase;
|
||
|
pWrapper->SetCodeBaseAddr(writer.outbase);
|
||
|
g_StackAlign = (g_StackUsage) ? ((g_StackUsage & 0xFFFFFFF0) + 16) - g_StackUsage : 0;
|
||
|
g_StackAlign = (g_StackAlign == 16) ? g_StackUsage : g_StackUsage + g_StackAlign;
|
||
|
#ifdef PLATFORM_POSIX
|
||
|
g_StackUsage = 0;
|
||
|
#elif defined PLATFORM_WINDOWS
|
||
|
g_StackUsage = 32; // Shadow space
|
||
|
#endif
|
||
|
g_RegDecoder = 0;
|
||
|
g_FloatRegDecoder = 0;
|
||
|
g_PODCount = 0;
|
||
|
g_FloatCount = 0;
|
||
|
g_ParamCount = 0;
|
||
|
Needs_Retbuf = false;
|
||
|
goto jit_rewind;
|
||
|
}
|
||
|
g_SPEngine->SetReadExecute(writer.outbase);
|
||
|
return writer.outbase;
|
||
|
}
|