1fe38c7473
This adds the ability for us to change the GCC version we use more flexibly.
707 lines
22 KiB
C++
707 lines
22 KiB
C++
/**
|
|
* vim: set ts=4 :
|
|
* =============================================================================
|
|
* SourceMod
|
|
* Copyright (C) 2004-2008 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>.
|
|
*
|
|
* Version: $Id$
|
|
*/
|
|
|
|
#include <limits.h>
|
|
#include <string.h>
|
|
#include <stdlib.h>
|
|
#include "jit_x86.h"
|
|
#include "opcode_helpers.h"
|
|
#include "x86_macros.h"
|
|
|
|
jitoffs_t Write_Execute_Function(JitWriter *jit)
|
|
{
|
|
/**
|
|
* The variables we're passed in:
|
|
* void *vars[], void *entry_func
|
|
*/
|
|
|
|
/**
|
|
* !NOTE!
|
|
* Currently, we do not accept ctx->frm as the new frame pointer.
|
|
* Instead, we copy the frame from the stack pointer.
|
|
* This is because we do not support resuming or sleeping!
|
|
*/
|
|
|
|
//push ebp
|
|
//mov ebp, esp
|
|
IA32_Push_Reg(jit, REG_EBP);
|
|
IA32_Mov_Reg_Rm(jit, REG_EBP, REG_ESP, MOD_REG);
|
|
|
|
//push esi
|
|
//push edi
|
|
//push ebx
|
|
IA32_Push_Reg(jit, REG_ESI);
|
|
IA32_Push_Reg(jit, REG_EDI);
|
|
IA32_Push_Reg(jit, REG_EBX);
|
|
|
|
/* Prep us for doing the real work */
|
|
//mov esi, [ebp+param0] ;get vars
|
|
//mov ecx, [ebp+param1] ;get entry addr
|
|
//mov eax, [esi+MEMORY] ;get memory base
|
|
//mov edx, [esi+CONTEXT] ;get context
|
|
IA32_Mov_Reg_Rm_Disp8(jit, REG_ESI, REG_EBP, 8 + 4*0);
|
|
IA32_Mov_Reg_Rm_Disp8(jit, REG_ECX, REG_EBP, 8 + 4*1);
|
|
IA32_Mov_Reg_Rm_Disp8(jit, REG_EAX, REG_ESI, AMX_INFO_MEMORY);
|
|
IA32_Mov_Reg_Rm_Disp8(jit, REG_EDX, REG_ESI, AMX_INFO_CONTEXT);
|
|
|
|
/* Set up run-time registers */
|
|
//mov edi, [edx+SP] ;non-reloc SP
|
|
//add edi, eax ;reloc SP
|
|
//mov ebp, eax ;DAT
|
|
//mov ebx, edi ;reloc FRM
|
|
//mov [esi+NSTACK], esp ;save ESP
|
|
IA32_Mov_Reg_Rm_Disp8(jit, REG_EDI, REG_EDX, offsetof(sp_context_t, sp));
|
|
IA32_Add_Rm_Reg(jit, REG_EDI, REG_EAX, MOD_REG);
|
|
IA32_Mov_Reg_Rm(jit, REG_EBP, REG_EAX, MOD_REG);
|
|
IA32_Mov_Reg_Rm(jit, REG_EBX, REG_EDI, MOD_REG);
|
|
IA32_Mov_Rm_Reg_Disp8(jit, REG_ESI, REG_ESP, AMX_INFO_NSTACK);
|
|
|
|
/* by now, everything is set up, so we can call into the plugin */
|
|
//call ecx
|
|
IA32_Call_Reg(jit, REG_ECX);
|
|
|
|
/* if the code flow gets to here, there was a normal return */
|
|
//mov ecx, [esi+RETVAL] ;get retval pointer
|
|
//mov [ecx], eax ;store retval from PRI
|
|
//mov eax, SP_ERROR_NONE ;set no error
|
|
IA32_Mov_Reg_Rm_Disp8(jit, REG_ECX, AMX_REG_INFO, AMX_INFO_RETVAL);
|
|
IA32_Mov_Rm_Reg(jit, REG_ECX, AMX_REG_PRI, MOD_MEM_REG);
|
|
IA32_Mov_Reg_Imm32(jit, REG_EAX, SP_ERROR_NONE);
|
|
|
|
/* save where error checking/halting functions should go to */
|
|
jitoffs_t offs_return = jit->get_outputpos();
|
|
//mov esp, [esi+NSTACK] ;restore stack pointer
|
|
IA32_Mov_Reg_Rm_Disp8(jit, REG_ESP, REG_ESI, AMX_INFO_NSTACK);
|
|
|
|
/* Restore SP */
|
|
//mov ecx, [esi+CONTEXT]
|
|
//sub edi, ebp
|
|
//mov [ecx+SP], edi
|
|
IA32_Mov_Reg_Rm_Disp8(jit, REG_ECX, REG_ESI, AMX_INFO_CONTEXT);
|
|
IA32_Sub_Reg_Rm(jit, REG_EDI, REG_EBP, MOD_REG);
|
|
IA32_Mov_Rm_Reg_Disp8(jit, REG_ECX, REG_EDI, offsetof(sp_context_t, sp));
|
|
|
|
//pop ebx
|
|
//pop edi
|
|
//pop esi
|
|
//pop ebp
|
|
//ret
|
|
IA32_Pop_Reg(jit, REG_EBX);
|
|
IA32_Pop_Reg(jit, REG_EDI);
|
|
IA32_Pop_Reg(jit, REG_ESI);
|
|
IA32_Pop_Reg(jit, REG_EBP);
|
|
IA32_Return(jit);
|
|
|
|
return offs_return;
|
|
}
|
|
|
|
void Write_GetError(JitWriter *jit)
|
|
{
|
|
//mov eax, [esi+info.context]
|
|
//mov eax, [eax+ctx.error]
|
|
//jmp [jit_return]
|
|
IA32_Mov_Reg_Rm_Disp8(jit, REG_EAX, AMX_REG_INFO, AMX_INFO_CONTEXT);
|
|
IA32_Mov_Reg_Rm_Disp8(jit, REG_EAX, REG_EAX, offsetof(sp_context_t, n_err));
|
|
IA32_Jump_Imm32_Abs(jit, g_Jit.GetReturnPoint());
|
|
}
|
|
|
|
void Write_SetError(JitWriter *jit, int error)
|
|
{
|
|
//mov eax, <error>
|
|
//jmp [jit_return]
|
|
IA32_Mov_Reg_Imm32(jit, REG_EAX, error);
|
|
IA32_Jump_Imm32_Abs(jit, g_Jit.GetReturnPoint());
|
|
}
|
|
|
|
void Write_Check_DivZero(JitWriter *jit, jit_uint8_t reg)
|
|
{
|
|
//test reg, reg
|
|
//jz :error
|
|
IA32_Test_Rm_Reg(jit, reg, reg, MOD_REG);
|
|
IA32_Jump_Cond_Imm32_Rel(jit, CC_Z, ((CompData *)jit->data)->jit_error_divzero);
|
|
}
|
|
|
|
void Write_CheckHeap_Min(JitWriter *jit)
|
|
{
|
|
/* Check if the stack went beyond the heap low.
|
|
* This usually means there was a compiler error.
|
|
* NOTE: Special optimization here.
|
|
* The heap low is always known ahead of time! :)
|
|
*/
|
|
CompData *data = (CompData *)jit->data;
|
|
//cmp [esi+info.heap], <heaplow>
|
|
//jb :error
|
|
IA32_Cmp_Rm_Imm32_Disp8(jit, AMX_REG_INFO, AMX_INFO_HEAP, data->plugin->data_size);
|
|
IA32_Jump_Cond_Imm32_Rel(jit, CC_B, data->jit_error_heapmin);
|
|
}
|
|
|
|
void Write_CheckHeap_Low(JitWriter *jit)
|
|
{
|
|
/* Check if the heap is trying to grow beyond the stack.
|
|
*/
|
|
//mov ecx, [esi+info.heap]
|
|
//lea ecx, [ebp+ecx+STACK_MARGIN]
|
|
//cmp ecx, edi
|
|
//ja :error ; I think this is right
|
|
IA32_Mov_Reg_Rm_Disp8(jit, AMX_REG_TMP, AMX_REG_INFO, AMX_INFO_HEAP);
|
|
IA32_Lea_Reg_DispRegMultImm8(jit, AMX_REG_TMP, AMX_REG_DAT, AMX_REG_TMP, NOSCALE, STACK_MARGIN);
|
|
IA32_Cmp_Reg_Rm(jit, AMX_REG_TMP, AMX_REG_STK, MOD_REG);
|
|
IA32_Jump_Cond_Imm32_Rel(jit, CC_A, ((CompData *)jit->data)->jit_error_heaplow);
|
|
}
|
|
|
|
void Write_CheckStack_Min(JitWriter *jit)
|
|
{
|
|
/* Check if the stack went beyond the stack top
|
|
* This usually means there was a compiler error.
|
|
*/
|
|
//cmp edi, [esi+info.stacktop]
|
|
//jae :error
|
|
IA32_Cmp_Reg_Rm_Disp8(jit, AMX_REG_STK, AMX_REG_INFO, AMX_INFO_STACKTOP);
|
|
IA32_Jump_Cond_Imm32_Rel(jit, CC_AE, ((CompData *)jit->data)->jit_error_stackmin);
|
|
}
|
|
|
|
void Write_CheckStack_Low(JitWriter *jit)
|
|
{
|
|
/* Check if the stack went beyond the heap boundary.
|
|
* Unfortunately this one isn't as quick as the other check.
|
|
* The stack margin check is important for sysreq.n having space.
|
|
*/
|
|
//mov ecx, [esi+info.heap]
|
|
//lea ecx, [ebp+ecx+STACK_MARGIN]
|
|
//cmp edi, ecx
|
|
//jb :error ; I think this is right
|
|
IA32_Mov_Reg_Rm_Disp8(jit, AMX_REG_TMP, AMX_REG_INFO, AMX_INFO_HEAP);
|
|
IA32_Lea_Reg_DispRegMultImm8(jit, AMX_REG_TMP, AMX_REG_DAT, AMX_REG_TMP, NOSCALE, STACK_MARGIN);
|
|
IA32_Cmp_Reg_Rm(jit, AMX_REG_STK, AMX_REG_TMP, MOD_REG);
|
|
IA32_Jump_Cond_Imm32_Rel(jit, CC_B, ((CompData *)jit->data)->jit_error_stacklow);
|
|
}
|
|
|
|
void Write_Check_VerifyAddr(JitWriter *jit, jit_uint8_t reg)
|
|
{
|
|
/* :TODO: Should this be checking for below heaplow?
|
|
* The old JIT did not.
|
|
*/
|
|
|
|
/**
|
|
* :TODO: If we can't find a nicer way of doing this,
|
|
* then scrap it on high optimizations. The second portion is not needed at all!
|
|
*/
|
|
|
|
/* Part 1: Check if we're in the memory bounds */
|
|
//cmp <reg>, <stpu>
|
|
//jae :error
|
|
IA32_Cmp_Rm_Imm32(jit, MOD_REG, reg, ((CompData *)jit->data)->plugin->mem_size);
|
|
IA32_Jump_Cond_Imm32_Rel(jit, CC_AE, ((CompData *)jit->data)->jit_error_memaccess);
|
|
|
|
/* Part 2: Check if we're in the invalid region between HP and SP */
|
|
jitoffs_t jmp;
|
|
//cmp <reg>, [esi+info.heap]
|
|
//jb :continue
|
|
//lea ecx, [ebp+<reg>]
|
|
//cmp edi, ecx
|
|
//jb :error
|
|
//:continue
|
|
IA32_Cmp_Reg_Rm_Disp8(jit, reg, AMX_REG_INFO, AMX_INFO_HEAP);
|
|
jmp = IA32_Jump_Cond_Imm8(jit, CC_B, 0);
|
|
IA32_Lea_Reg_DispRegMultImm8(jit, AMX_REG_TMP, AMX_REG_DAT, reg, NOSCALE, 0);
|
|
IA32_Cmp_Reg_Rm(jit, AMX_REG_TMP, AMX_REG_STK, MOD_REG);
|
|
IA32_Jump_Cond_Imm32_Rel(jit, CC_B, ((CompData *)jit->data)->jit_error_memaccess);
|
|
IA32_Send_Jump8_Here(jit, jmp);
|
|
}
|
|
|
|
void Macro_PushN_Addr(JitWriter *jit, int i)
|
|
{
|
|
//push eax
|
|
//mov eax, [esi+frm]
|
|
//loop i times:
|
|
// lea ecx, [eax+<val>]
|
|
// mov [edi-4*i], ecx
|
|
//sub edi, 4*N
|
|
//pop eax
|
|
|
|
cell_t val;
|
|
int n = 1;
|
|
IA32_Push_Reg(jit, AMX_REG_PRI);
|
|
IA32_Mov_Reg_Rm(jit, AMX_REG_PRI, AMX_REG_INFO, MOD_MEM_REG);
|
|
do
|
|
{
|
|
val = jit->read_cell();
|
|
if (val <= SCHAR_MAX && val >= SCHAR_MIN)
|
|
IA32_Lea_DispRegImm8(jit, AMX_REG_TMP, AMX_REG_PRI, (jit_int8_t)val);
|
|
else
|
|
IA32_Lea_DispRegImm32(jit, AMX_REG_TMP, AMX_REG_PRI, val);
|
|
IA32_Mov_Rm_Reg_Disp8(jit, AMX_REG_STK, AMX_REG_TMP, -4*n);
|
|
} while (n++ < i);
|
|
IA32_Sub_Rm_Imm8(jit, AMX_REG_STK, 4*i, MOD_REG);
|
|
IA32_Pop_Reg(jit, AMX_REG_PRI);
|
|
}
|
|
|
|
void Macro_PushN_S(JitWriter *jit, int i)
|
|
{
|
|
//loop i times:
|
|
// mov ecx, [ebx+<val>]
|
|
// mov [edi-4*i], ecx
|
|
//sub edi, 4*N
|
|
|
|
cell_t val;
|
|
int n = 1;
|
|
do
|
|
{
|
|
val = jit->read_cell();
|
|
if (val <= SCHAR_MAX && val >= SCHAR_MIN)
|
|
IA32_Mov_Reg_Rm_Disp8(jit, AMX_REG_TMP, AMX_REG_FRM, (jit_int8_t)val);
|
|
else
|
|
IA32_Mov_Reg_Rm_Disp32(jit, AMX_REG_TMP, AMX_REG_FRM, val);
|
|
IA32_Mov_Rm_Reg_Disp8(jit, AMX_REG_STK, AMX_REG_TMP, -4*n);
|
|
} while (n++ < i);
|
|
IA32_Sub_Rm_Imm8(jit, AMX_REG_STK, 4*i, MOD_REG);
|
|
}
|
|
|
|
void Macro_PushN_C(JitWriter *jit, int i)
|
|
{
|
|
//loop i times:
|
|
// mov [edi-4*i], <val>
|
|
//sub edi, 4*N
|
|
|
|
int n = 1;
|
|
do
|
|
{
|
|
IA32_Mov_Rm_Imm32_Disp8(jit, AMX_REG_STK, jit->read_cell(), -4*n);
|
|
} while (n++ < i);
|
|
IA32_Sub_Rm_Imm8(jit, AMX_REG_STK, 4*i, MOD_REG);
|
|
}
|
|
|
|
void Macro_PushN(JitWriter *jit, int i)
|
|
{
|
|
//loop i times:
|
|
// mov ecx, [ebp+<val>]
|
|
// mov [edi-4*i], ecx
|
|
//sub edi, 4*N
|
|
|
|
cell_t val;
|
|
int n = 1;
|
|
do
|
|
{
|
|
val = jit->read_cell();
|
|
if (val <= SCHAR_MAX && val >= SCHAR_MIN)
|
|
IA32_Mov_Reg_Rm_Disp8(jit, AMX_REG_TMP, AMX_REG_DAT, (jit_int8_t)val);
|
|
else
|
|
IA32_Mov_Reg_Rm_Disp32(jit, AMX_REG_TMP, AMX_REG_DAT, val);
|
|
IA32_Mov_Rm_Reg_Disp8(jit, AMX_REG_STK, AMX_REG_TMP, -4*n);
|
|
} while (n++ < i);
|
|
IA32_Sub_Rm_Imm8(jit, AMX_REG_STK, 4*i, MOD_REG);
|
|
}
|
|
|
|
void WriteOp_Sysreq_C_Function(JitWriter *jit)
|
|
{
|
|
/* The small daddy of the big daddy of opcodes.
|
|
* ecx - native index
|
|
*/
|
|
CompData *data = (CompData *)jit->data;
|
|
|
|
/* save registers we will need */
|
|
//push edx
|
|
IA32_Push_Reg(jit, AMX_REG_ALT);
|
|
|
|
/* Align the stack to 16 bytes */
|
|
//push ebx
|
|
//mov ebx, esp
|
|
//and esp, 0xFFFFFF0
|
|
//sub esp, 4
|
|
IA32_Push_Reg(jit, REG_EBX);
|
|
IA32_Mov_Reg_Rm(jit, REG_EBX, REG_ESP, MOD_REG);
|
|
IA32_And_Rm_Imm8(jit, REG_ESP, MOD_REG, -16);
|
|
IA32_Sub_Rm_Imm8(jit, REG_ESP, 4, MOD_REG);
|
|
|
|
/* push some callback stuff */
|
|
//push edi ; stack
|
|
//push ecx ; native index
|
|
IA32_Push_Reg(jit, AMX_REG_STK);
|
|
IA32_Push_Reg(jit, REG_ECX);
|
|
|
|
/* Relocate stack, heap, frm information, then store back */
|
|
//mov eax, [esi+context]
|
|
//mov ecx, [esi+hea]
|
|
//sub edi, ebp
|
|
//mov [eax+hp], ecx
|
|
//mov ecx, [esi]
|
|
//mov [eax+sp], edi
|
|
//mov [eax+frm], ecx
|
|
IA32_Mov_Reg_Rm_Disp8(jit, REG_EAX, AMX_REG_INFO, AMX_INFO_CONTEXT);
|
|
IA32_Mov_Reg_Rm_Disp8(jit, AMX_REG_TMP, AMX_REG_INFO, AMX_INFO_HEAP);
|
|
IA32_Sub_Reg_Rm(jit, AMX_REG_STK, AMX_REG_DAT, MOD_REG);
|
|
IA32_Mov_Rm_Reg_Disp8(jit, REG_EAX, AMX_REG_TMP, offsetof(sp_context_t, hp));
|
|
IA32_Mov_Reg_Rm(jit, AMX_REG_TMP, AMX_INFO_FRM, MOD_MEM_REG);
|
|
IA32_Mov_Rm_Reg_Disp8(jit, REG_EAX, AMX_REG_STK, offsetof(sp_context_t, sp));
|
|
IA32_Mov_Rm_Reg_Disp8(jit, REG_EAX, AMX_REG_TMP, offsetof(sp_context_t, frm));
|
|
|
|
/* finally, push the last parameter and make the call */
|
|
//push eax ; context
|
|
//call NativeCallback
|
|
IA32_Push_Reg(jit, REG_EAX);
|
|
jitoffs_t call = IA32_Call_Imm32(jit, 0);
|
|
IA32_Write_Jump32_Abs(jit, call, (void *)NativeCallback);
|
|
|
|
/* Test for error */
|
|
//mov ecx, [esi+context]
|
|
//cmp [ecx+err], 0
|
|
//jnz :error
|
|
IA32_Mov_Reg_Rm_Disp8(jit, AMX_REG_TMP, AMX_REG_INFO, AMX_INFO_CONTEXT);
|
|
IA32_Cmp_Rm_Disp8_Imm8(jit, AMX_REG_TMP, offsetof(sp_context_t, n_err), 0);
|
|
IA32_Jump_Cond_Imm32_Rel(jit, CC_NZ, data->jit_extern_error);
|
|
|
|
/* restore what we damaged */
|
|
//mov esp, ebx
|
|
//pop ebx
|
|
//add edi, ebp
|
|
//pop edx
|
|
IA32_Mov_Reg_Rm(jit, REG_ESP, REG_EBX, MOD_REG);
|
|
IA32_Pop_Reg(jit, REG_EBX);
|
|
IA32_Add_Reg_Rm(jit, AMX_REG_STK, AMX_REG_DAT, MOD_REG);
|
|
IA32_Pop_Reg(jit, AMX_REG_ALT);
|
|
|
|
//ret
|
|
IA32_Return(jit);
|
|
}
|
|
|
|
typedef struct array_creation_s
|
|
{
|
|
const cell_t *dim_list; /* Dimension sizes */
|
|
cell_t dim_count; /* Number of dimensions */
|
|
cell_t *data_offs; /* Current offset AFTER the indirection vectors (data) */
|
|
cell_t *base; /* array base */
|
|
} array_creation_t;
|
|
|
|
cell_t _GenerateArrayIndirectionVectors(array_creation_t *ar, int dim, cell_t cur_offs)
|
|
{
|
|
cell_t write_offs = cur_offs;
|
|
cell_t *data_offs = ar->data_offs;
|
|
|
|
cur_offs += ar->dim_list[dim];
|
|
|
|
/**
|
|
* Dimension n-x where x > 2 will have sub-vectors.
|
|
* Otherwise, we just need to reference the data section.
|
|
*/
|
|
if (ar->dim_count > 2 && dim < ar->dim_count - 2)
|
|
{
|
|
/**
|
|
* For each index at this dimension, write offstes to our sub-vectors.
|
|
* After we write one sub-vector, we generate its sub-vectors recursively.
|
|
* At the end, we're given the next offset we can use.
|
|
*/
|
|
for (int i = 0; i < ar->dim_list[dim]; i++)
|
|
{
|
|
ar->base[write_offs] = (cur_offs - write_offs) * sizeof(cell_t);
|
|
write_offs++;
|
|
cur_offs = _GenerateArrayIndirectionVectors(ar, dim + 1, cur_offs);
|
|
}
|
|
} else {
|
|
/**
|
|
* In this section, there are no sub-vectors, we need to write offsets
|
|
* to the data. This is separate so the data stays in one big chunk.
|
|
* The data offset will increment by the size of the last dimension,
|
|
* because that is where the data is finally computed as.
|
|
*/
|
|
for (int i = 0; i < ar->dim_list[dim]; i++)
|
|
{
|
|
ar->base[write_offs] = (*data_offs - write_offs) * sizeof(cell_t);
|
|
write_offs++;
|
|
*data_offs = *data_offs + ar->dim_list[dim + 1];
|
|
}
|
|
}
|
|
|
|
return cur_offs;
|
|
}
|
|
|
|
static cell_t calc_indirection(const array_creation_t *ar, cell_t dim)
|
|
{
|
|
cell_t size = ar->dim_list[dim];
|
|
|
|
if (dim < ar->dim_count - 2)
|
|
{
|
|
size += ar->dim_list[dim] * calc_indirection(ar, dim + 1);
|
|
}
|
|
|
|
return size;
|
|
}
|
|
|
|
void GenerateArrayIndirectionVectors(cell_t *arraybase, cell_t dims[], cell_t _dimcount, bool autozero)
|
|
{
|
|
array_creation_t ar;
|
|
cell_t data_offs;
|
|
|
|
/* Reverse the dimensions */
|
|
cell_t dim_list[sDIMEN_MAX];
|
|
int cur_dim = 0;
|
|
for (int i = _dimcount - 1; i >= 0; i--)
|
|
{
|
|
dim_list[cur_dim++] = dims[i];
|
|
}
|
|
|
|
ar.base = arraybase;
|
|
ar.dim_list = dim_list;
|
|
ar.dim_count = _dimcount;
|
|
ar.data_offs = &data_offs;
|
|
|
|
data_offs = calc_indirection(&ar, 0);
|
|
|
|
_GenerateArrayIndirectionVectors(&ar, 0, 0);
|
|
}
|
|
|
|
/**
|
|
* A few notes about this function.
|
|
* I was more concerned about efficient use of registers here, rather than
|
|
* fine-tuned optimization. The reason is that the code is already complicated,
|
|
* and it is very easy to mess up.
|
|
*/
|
|
void WriteIntrinsic_GenArray(JitWriter *jit)
|
|
{
|
|
jitoffs_t err1, err2;
|
|
|
|
/**
|
|
* save important values
|
|
*/
|
|
//push ebx
|
|
//push eax
|
|
//push edx
|
|
//push ecx ;value is referenced on stack
|
|
IA32_Push_Reg(jit, REG_EBX);
|
|
IA32_Push_Reg(jit, REG_EAX);
|
|
IA32_Push_Reg(jit, REG_EDX);
|
|
IA32_Push_Reg(jit, REG_ECX);
|
|
|
|
/**
|
|
* Calculate how many cells will be needed.
|
|
*/
|
|
//mov edx, [edi] ;get last dimension's count
|
|
//mov eax, 1 ;position at second to last dimension
|
|
//:loop
|
|
//cmp eax, [esp] ;compare to # of params
|
|
//jae :done ;end loop if done
|
|
//mov ecx, [edi+eax*4] ;get dimension size
|
|
//imul edx, ecx ;multiply by size
|
|
//add eax, 1 ;increment
|
|
//add edx, ecx ;add size (indirection vector)
|
|
//jmp :loop ;jump back
|
|
//:done
|
|
IA32_Mov_Reg_Rm(jit, REG_EDX, AMX_REG_STK, MOD_MEM_REG);
|
|
IA32_Mov_Reg_Imm32(jit, REG_EAX, 1);
|
|
jitoffs_t loop1 = jit->get_outputpos();
|
|
IA32_Cmp_Reg_Rm_ESP(jit, REG_EAX);
|
|
jitoffs_t done1 = IA32_Jump_Cond_Imm8(jit, CC_AE, 0);
|
|
IA32_Mov_Reg_Rm_Disp_Reg(jit, REG_ECX, AMX_REG_STK, REG_EAX, SCALE4);
|
|
IA32_IMul_Reg_Rm(jit, REG_EDX, REG_ECX, MOD_REG);
|
|
IA32_Add_Rm_Imm8(jit, REG_EAX, 1, MOD_REG);
|
|
IA32_Add_Reg_Rm(jit, REG_EDX, REG_ECX, MOD_REG);
|
|
IA32_Write_Jump8(jit, IA32_Jump_Imm8(jit, loop1), loop1);
|
|
IA32_Send_Jump8_Here(jit, done1);
|
|
|
|
/* Test if we have heap space for this */
|
|
//mov eax, [esi+info.heap] ;get heap pointer
|
|
//lea eax, [eax+edx*4] ;new heap pointer
|
|
//cmp eax, [esi+info.datasz] ;compare to heap low
|
|
//jbe :error ;die if we hit this (it should always be >)
|
|
//add eax, ebp ;relocate to stack
|
|
//cmp eax, edi ;die if above the stack pointer
|
|
//jae :error
|
|
IA32_Mov_Reg_Rm_Disp8(jit, REG_EAX, AMX_REG_INFO, AMX_INFO_HEAP);
|
|
IA32_Lea_Reg_DispRegMult(jit, REG_EAX, REG_EAX, REG_EDX, SCALE4);
|
|
IA32_Cmp_Reg_Rm_Disp8(jit, REG_EAX, AMX_REG_INFO, AMX_INFO_DATASIZE);
|
|
err1 = IA32_Jump_Cond_Imm32(jit, CC_BE, 0);
|
|
IA32_Add_Reg_Rm(jit, REG_EAX, AMX_REG_DAT, MOD_REG);
|
|
IA32_Cmp_Reg_Rm(jit, REG_EAX, AMX_REG_STK, MOD_REG);
|
|
err2 = IA32_Jump_Cond_Imm32(jit, CC_AE, 0);
|
|
|
|
/* Prepare for indirection iteration */
|
|
//mov eax, [esi+info.heap] ;get heap pointer
|
|
//lea ebx, [eax+edx*4] ;new heap pointer
|
|
//mov [esi+info.heap], ebx ;store back
|
|
//push eax ;save heap pointer - we need it
|
|
IA32_Mov_Reg_Rm_Disp8(jit, REG_EAX, AMX_REG_INFO, AMX_INFO_HEAP);
|
|
IA32_Lea_Reg_DispRegMult(jit, REG_EBX, REG_EAX, REG_EDX, SCALE4);
|
|
IA32_Mov_Rm_Reg_Disp8(jit, AMX_REG_INFO, REG_EBX, AMX_INFO_HEAP);
|
|
IA32_Push_Reg(jit, REG_EAX);
|
|
|
|
WriteOp_Tracker_Push_Reg(jit, REG_EDX);
|
|
|
|
/* This part is too messy to do in straight assembly.
|
|
* I'm letting the compiler handle it and thus it's in C.
|
|
*/
|
|
//lea ebx, [ebp+eax] ;get base pointer
|
|
//push dword [esp-8] ;push autozero
|
|
//push dword [esp-8] ;push dimension count
|
|
//push edi ;push dim array
|
|
//push ebx
|
|
//call GenerateArrayIndirectionVectors
|
|
//add esp, 4*4
|
|
IA32_Lea_Reg_DispRegMult(jit, REG_EBX, REG_EAX, REG_EBP, NOSCALE);
|
|
IA32_Push_Rm_Disp8_ESP(jit, 8);
|
|
IA32_Push_Rm_Disp8_ESP(jit, 8);
|
|
IA32_Push_Reg(jit, REG_EDI);
|
|
IA32_Push_Reg(jit, REG_EBX);
|
|
IA32_Write_Jump32_Abs(jit, IA32_Call_Imm32(jit, 0), (void *)&GenerateArrayIndirectionVectors);
|
|
IA32_Add_Rm_Imm8(jit, REG_ESP, 4*4, MOD_REG);
|
|
|
|
/* Store the heap pointer back into the stack */
|
|
//pop eax ;restore heap pointer
|
|
//pop ecx ;restore param count
|
|
//lea edi, [edi+ecx*4-4] ;pop params-4 off the stack
|
|
//mov [edi], eax ;store back the heap pointer
|
|
IA32_Pop_Reg(jit, REG_EAX);
|
|
IA32_Pop_Reg(jit, REG_ECX);
|
|
IA32_Lea_Reg_DispRegMultImm8(jit, AMX_REG_STK, AMX_REG_STK, REG_ECX, SCALE4, -4);
|
|
IA32_Mov_Rm_Reg(jit, AMX_REG_STK, REG_EAX, MOD_MEM_REG);
|
|
|
|
/* Return to caller */
|
|
//pop edx
|
|
//pop eax
|
|
//pop ebx
|
|
//ret
|
|
IA32_Pop_Reg(jit, REG_ECX);
|
|
IA32_Pop_Reg(jit, REG_EAX);
|
|
IA32_Pop_Reg(jit, REG_EBX);
|
|
IA32_Return(jit);
|
|
|
|
//:error
|
|
IA32_Send_Jump32_Here(jit, err1);
|
|
IA32_Send_Jump32_Here(jit, err2);
|
|
Write_SetError(jit, SP_ERROR_ARRAY_TOO_BIG);
|
|
}
|
|
|
|
void WriteOp_Tracker_Push_Reg(JitWriter *jit, uint8_t reg)
|
|
{
|
|
/* Save registers that may be damaged by the call */
|
|
//push eax
|
|
//push ecx
|
|
//push edi
|
|
//lea edi, [<reg>*4] ; we want the count in bytes not in cells
|
|
IA32_Push_Reg(jit, AMX_REG_PRI);
|
|
if (reg == REG_ECX)
|
|
{
|
|
IA32_Push_Reg(jit, AMX_REG_TMP);
|
|
}
|
|
IA32_Push_Reg(jit, AMX_REG_STK);
|
|
IA32_Lea_Reg_RegMultImm32(jit, REG_EDI, reg, SCALE4, 0);
|
|
|
|
/* Get the context ptr, push it and call the check */
|
|
//mov eax, [esi+context]
|
|
//push eax
|
|
//call JIT_VerifyOrAllocateTracker
|
|
IA32_Mov_Reg_Rm_Disp8(jit, REG_EAX, AMX_REG_INFO, AMX_INFO_CONTEXT);
|
|
IA32_Push_Reg(jit, REG_EAX);
|
|
jitoffs_t call = IA32_Call_Imm32(jit, 0);
|
|
IA32_Write_Jump32_Abs(jit, call, (void *)JIT_VerifyOrAllocateTracker);
|
|
|
|
/* Check for errors */
|
|
//cmp eax, 0
|
|
//jnz :error
|
|
IA32_Cmp_Rm_Imm8(jit, MOD_REG, REG_EAX, 0);
|
|
IA32_Jump_Cond_Imm32_Abs(jit, CC_NZ, g_Jit.GetReturnPoint());
|
|
|
|
/* Restore */
|
|
//pop eax
|
|
IA32_Pop_Reg(jit, REG_EAX);
|
|
|
|
/* Push the register into the stack and increment pCur */
|
|
//mov edx, [eax+vm[]]
|
|
//mov eax, [edx+pcur]
|
|
//add [edx+pcur], 4
|
|
//mov [eax], edi
|
|
IA32_Mov_Reg_Rm_Disp8(jit, REG_EDX, REG_EAX, offsetof(sp_context_t, vm[JITVARS_TRACKER]));
|
|
IA32_Mov_Reg_Rm_Disp8(jit, REG_EAX, REG_EDX, offsetof(tracker_t, pCur));
|
|
IA32_Add_Rm_Imm8_Disp8(jit, REG_EDX, 4, offsetof(tracker_t, pCur));
|
|
IA32_Mov_Rm_Reg(jit, REG_EAX, REG_EDI, MOD_MEM_REG);
|
|
|
|
/* Restore PRI, ALT and STK */
|
|
//pop edi
|
|
//pop ecx
|
|
//pop eax
|
|
IA32_Pop_Reg(jit, AMX_REG_STK);
|
|
if (reg == REG_ECX)
|
|
{
|
|
IA32_Pop_Reg(jit, AMX_REG_TMP);
|
|
}
|
|
IA32_Pop_Reg(jit, AMX_REG_PRI);
|
|
}
|
|
|
|
int JIT_VerifyOrAllocateTracker(sp_context_t *ctx)
|
|
{
|
|
tracker_t *trk = (tracker_t *)(ctx->vm[JITVARS_TRACKER]);
|
|
|
|
if ((size_t)(trk->pCur - trk->pBase) >= trk->size)
|
|
{
|
|
return SP_ERROR_TRACKER_BOUNDS;
|
|
}
|
|
|
|
if (trk->pCur+1 - (trk->pBase + trk->size) == 0)
|
|
{
|
|
size_t disp = trk->size - 1;
|
|
trk->size *= 2;
|
|
trk->pBase = (ucell_t *)realloc(trk->pBase, trk->size * sizeof(cell_t));
|
|
|
|
if (!trk->pBase)
|
|
{
|
|
return SP_ERROR_TRACKER_BOUNDS;
|
|
}
|
|
|
|
trk->pCur = trk->pBase + disp;
|
|
}
|
|
|
|
return SP_ERROR_NONE;
|
|
}
|
|
|
|
int JIT_VerifyLowBoundTracker(sp_context_t *ctx)
|
|
{
|
|
tracker_t *trk = (tracker_t *)(ctx->vm[JITVARS_TRACKER]);
|
|
|
|
if (trk->pCur <= trk->pBase)
|
|
{
|
|
return SP_ERROR_TRACKER_BOUNDS;
|
|
}
|
|
|
|
return SP_ERROR_NONE;
|
|
}
|
|
|
|
void AlignMe(JitWriter *jit)
|
|
{
|
|
jitoffs_t cur_offs = jit->get_outputpos();
|
|
jitoffs_t offset = ((cur_offs & 0xFFFFFFF0) + 16) - cur_offs;
|
|
|
|
if (offset)
|
|
{
|
|
for (jit_uint32_t i=0; i<offset; i++)
|
|
{
|
|
jit->write_ubyte(IA32_INT3);
|
|
}
|
|
}
|
|
}
|