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;
 | 
						|
}
 |