and you thought you'd never see the day.. sysreq.n verified!
--HG-- extra : convert_revision : svn%3A39bc706e-5318-0410-9160-8a85361fbb7c/trunk%40117
This commit is contained in:
parent
c06a526165
commit
fb7942ee4d
@ -60,20 +60,24 @@ inline void WriteOp_Zero(JitWriter *jit)
|
|||||||
//mov [ebp+<val>], 0
|
//mov [ebp+<val>], 0
|
||||||
cell_t val = jit->read_cell();
|
cell_t val = jit->read_cell();
|
||||||
if (val < SCHAR_MAX && val > SCHAR_MIN)
|
if (val < SCHAR_MAX && val > SCHAR_MIN)
|
||||||
|
{
|
||||||
IA32_Mov_Rm_Imm32_Disp8(jit, AMX_REG_DAT, 0, (jit_int8_t)val);
|
IA32_Mov_Rm_Imm32_Disp8(jit, AMX_REG_DAT, 0, (jit_int8_t)val);
|
||||||
else
|
} else {
|
||||||
IA32_Mov_Rm_Imm32_Disp32(jit, AMX_REG_DAT, 0, val);
|
IA32_Mov_Rm_Imm32_Disp32(jit, AMX_REG_DAT, 0, val);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
inline void WriteOp_Zero_S(JitWriter *jit)
|
inline void WriteOp_Zero_S(JitWriter *jit)
|
||||||
{
|
{
|
||||||
//mov [ebx+<val>], 0
|
//mov [ebx+<val>], 0
|
||||||
cell_t val = jit->read_cell();
|
cell_t val = jit->read_cell();
|
||||||
if (val < SCHAR_MAX && val > SCHAR_MIN)
|
if (val < SCHAR_MAX && val > SCHAR_MIN)
|
||||||
|
{
|
||||||
IA32_Mov_Rm_Imm32_Disp8(jit, AMX_REG_FRM, 0, (jit_int8_t)val);
|
IA32_Mov_Rm_Imm32_Disp8(jit, AMX_REG_FRM, 0, (jit_int8_t)val);
|
||||||
else
|
} else {
|
||||||
IA32_Mov_Rm_Imm32_Disp32(jit, AMX_REG_FRM, 0, val);
|
IA32_Mov_Rm_Imm32_Disp32(jit, AMX_REG_FRM, 0, val);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
inline void WriteOp_Push_S(JitWriter *jit)
|
inline void WriteOp_Push_S(JitWriter *jit)
|
||||||
{
|
{
|
||||||
@ -85,9 +89,11 @@ inline void WriteOp_Push_S(JitWriter *jit)
|
|||||||
IA32_Sub_Rm_Imm8(jit, AMX_REG_STK, 4, MOD_REG);
|
IA32_Sub_Rm_Imm8(jit, AMX_REG_STK, 4, MOD_REG);
|
||||||
//optimize encoding a bit...
|
//optimize encoding a bit...
|
||||||
if (val < SCHAR_MAX && val > SCHAR_MIN)
|
if (val < SCHAR_MAX && val > SCHAR_MIN)
|
||||||
|
{
|
||||||
IA32_Mov_Reg_Rm_Disp8(jit, AMX_REG_TMP, AMX_REG_FRM, (jit_int8_t)val);
|
IA32_Mov_Reg_Rm_Disp8(jit, AMX_REG_TMP, AMX_REG_FRM, (jit_int8_t)val);
|
||||||
else
|
} else {
|
||||||
IA32_Mov_Reg_Rm_Disp32(jit, AMX_REG_TMP, AMX_REG_FRM, val);
|
IA32_Mov_Reg_Rm_Disp32(jit, AMX_REG_TMP, AMX_REG_FRM, val);
|
||||||
|
}
|
||||||
IA32_Mov_Rm_Reg(jit, AMX_REG_STK, AMX_REG_TMP, MOD_MEM_REG);
|
IA32_Mov_Rm_Reg(jit, AMX_REG_STK, AMX_REG_TMP, MOD_MEM_REG);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1406,8 +1412,14 @@ inline void WriteOp_Sysreq_N_NoInline(JitWriter *jit)
|
|||||||
/* store the number of parameters on the stack,
|
/* store the number of parameters on the stack,
|
||||||
* and store the native index as well.
|
* and store the native index as well.
|
||||||
*/
|
*/
|
||||||
cell_t num_params = jit->read_cell();
|
|
||||||
cell_t native_index = jit->read_cell();
|
cell_t native_index = jit->read_cell();
|
||||||
|
cell_t num_params = jit->read_cell();
|
||||||
|
|
||||||
|
if ((uint32_t)native_index >= ((CompData*)jit->data)->plugin->info.natives_num)
|
||||||
|
{
|
||||||
|
((CompData *)jit->data)->error_set = SP_ERR_INSTRUCTION_PARAM;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
//mov eax, <num_params>
|
//mov eax, <num_params>
|
||||||
//mov ecx, <native_index>
|
//mov ecx, <native_index>
|
||||||
@ -1421,10 +1433,16 @@ inline void WriteOp_Sysreq_N_NoInline(JitWriter *jit)
|
|||||||
inline void WriteOp_Sysreq_N(JitWriter *jit)
|
inline void WriteOp_Sysreq_N(JitWriter *jit)
|
||||||
{
|
{
|
||||||
/* The big daddy of opcodes. */
|
/* The big daddy of opcodes. */
|
||||||
cell_t num_params = jit->read_cell();
|
|
||||||
cell_t native_index = jit->read_cell();
|
cell_t native_index = jit->read_cell();
|
||||||
|
cell_t num_params = jit->read_cell();
|
||||||
CompData *data = (CompData *)jit->data;
|
CompData *data = (CompData *)jit->data;
|
||||||
|
|
||||||
|
if ((uint32_t)native_index >= data->plugin->info.natives_num)
|
||||||
|
{
|
||||||
|
data->error_set = SP_ERR_INSTRUCTION_PARAM;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
/* store the number of parameters on the stack */
|
/* store the number of parameters on the stack */
|
||||||
//mov [edi-4], num_params
|
//mov [edi-4], num_params
|
||||||
//sub edi, 4
|
//sub edi, 4
|
||||||
@ -1463,7 +1481,15 @@ inline void WriteOp_Sysreq_N(JitWriter *jit)
|
|||||||
//call NativeCallback
|
//call NativeCallback
|
||||||
IA32_Push_Reg(jit, REG_EAX);
|
IA32_Push_Reg(jit, REG_EAX);
|
||||||
jitoffs_t call = IA32_Call_Imm32(jit, 0);
|
jitoffs_t call = IA32_Call_Imm32(jit, 0);
|
||||||
IA32_Write_Jump32(jit, call, (jitoffs_t)(char *)&NativeCallback);
|
IA32_Write_Jump32_Abs(jit, call, NativeCallback);
|
||||||
|
|
||||||
|
/* check for errors */
|
||||||
|
//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_Imm32_Disp8(jit, AMX_REG_TMP, offsetof(sp_context_t, err), 0);
|
||||||
|
IA32_Jump_Cond_Imm32_Abs(jit, CC_NZ, data->jit_extern_error);
|
||||||
|
|
||||||
/* restore what we damaged */
|
/* restore what we damaged */
|
||||||
//add esp, 4*3
|
//add esp, 4*3
|
||||||
@ -1473,12 +1499,6 @@ inline void WriteOp_Sysreq_N(JitWriter *jit)
|
|||||||
IA32_Add_Rm_Reg(jit, AMX_REG_STK, AMX_REG_DAT, MOD_REG);
|
IA32_Add_Rm_Reg(jit, AMX_REG_STK, AMX_REG_DAT, MOD_REG);
|
||||||
IA32_Pop_Reg(jit, AMX_REG_ALT);
|
IA32_Pop_Reg(jit, AMX_REG_ALT);
|
||||||
|
|
||||||
/* check for errors */
|
|
||||||
//test eax, eax
|
|
||||||
//jne :error
|
|
||||||
IA32_Test_Rm_Reg(jit, REG_EAX, REG_EAX, MOD_REG);
|
|
||||||
IA32_Jump_Cond_Imm32_Abs(jit, CC_NE, data->jit_return);
|
|
||||||
|
|
||||||
/* pop the stack. do not check the margins.
|
/* pop the stack. do not check the margins.
|
||||||
* Note that this is not a true macro - we don't bother to
|
* Note that this is not a true macro - we don't bother to
|
||||||
* set ALT here because nothing will be using it.
|
* set ALT here because nothing will be using it.
|
||||||
@ -1503,11 +1523,18 @@ inline void WriteOp_Sysreq_N(JitWriter *jit)
|
|||||||
|
|
||||||
cell_t NativeCallback(sp_context_t *ctx, ucell_t native_idx, cell_t *params)
|
cell_t NativeCallback(sp_context_t *ctx, ucell_t native_idx, cell_t *params)
|
||||||
{
|
{
|
||||||
/* :TODO: fill this out... */
|
sp_native_t *native = &ctx->natives[native_idx];
|
||||||
|
|
||||||
|
/* Technically both aren't needed, I guess */
|
||||||
|
if (native->status == SP_NATIVE_NONE)
|
||||||
|
{
|
||||||
|
ctx->err = SP_ERR_NATIVE_PENDING;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return native->pfn(ctx, params);
|
||||||
|
}
|
||||||
|
|
||||||
jitoffs_t RelocLookup(JitWriter *jit, cell_t pcode_offs, bool relative)
|
jitoffs_t RelocLookup(JitWriter *jit, cell_t pcode_offs, bool relative)
|
||||||
{
|
{
|
||||||
if (jit->outptr)
|
if (jit->outptr)
|
||||||
@ -1529,6 +1556,33 @@ jitoffs_t RelocLookup(JitWriter *jit, cell_t pcode_offs, bool relative)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void WriteErrorRoutines(CompData *data, JitWriter *jit)
|
||||||
|
{
|
||||||
|
data->jit_error_divzero = jit->get_outputpos();
|
||||||
|
Write_SetError(jit, SP_ERR_DIVIDE_BY_ZERO);
|
||||||
|
|
||||||
|
data->jit_error_stacklow = jit->get_outputpos();
|
||||||
|
Write_SetError(jit, SP_ERR_STACKLOW);
|
||||||
|
|
||||||
|
data->jit_error_stackmin = jit->get_outputpos();
|
||||||
|
Write_SetError(jit, SP_ERR_STACKMIN);
|
||||||
|
|
||||||
|
data->jit_error_bounds = jit->get_outputpos();
|
||||||
|
Write_SetError(jit, SP_ERR_ARRAY_BOUNDS);
|
||||||
|
|
||||||
|
data->jit_error_memaccess = jit->get_outputpos();
|
||||||
|
Write_SetError(jit, SP_ERR_MEMACCESS);
|
||||||
|
|
||||||
|
data->jit_error_heaplow = jit->get_outputpos();
|
||||||
|
Write_SetError(jit, SP_ERR_HEAPLOW);
|
||||||
|
|
||||||
|
data->jit_error_heapmin = jit->get_outputpos();
|
||||||
|
Write_SetError(jit, SP_ERR_HEAPMIN);
|
||||||
|
|
||||||
|
data->jit_extern_error = jit->get_outputpos();
|
||||||
|
Write_GetError(jit);
|
||||||
|
}
|
||||||
|
|
||||||
sp_context_t *JITX86::CompileToContext(ICompilation *co, int *err)
|
sp_context_t *JITX86::CompileToContext(ICompilation *co, int *err)
|
||||||
{
|
{
|
||||||
CompData *data = (CompData *)co;
|
CompData *data = (CompData *)co;
|
||||||
@ -1536,61 +1590,17 @@ sp_context_t *JITX86::CompileToContext(ICompilation *co, int *err)
|
|||||||
|
|
||||||
/* The first phase is to browse */
|
/* The first phase is to browse */
|
||||||
uint8_t *code = plugin->pcode;
|
uint8_t *code = plugin->pcode;
|
||||||
uint8_t *cip;
|
|
||||||
uint8_t *end_cip = plugin->pcode + plugin->pcode_size;
|
uint8_t *end_cip = plugin->pcode + plugin->pcode_size;
|
||||||
OPCODE op;
|
OPCODE op;
|
||||||
int op_c;
|
|
||||||
|
|
||||||
/* FIRST PASS (light load) - Get initial opcode information
|
|
||||||
* :TODO: remove this pass soon, it's not needed anymore!
|
|
||||||
*/
|
|
||||||
for (cip = code; cip < end_cip;)
|
|
||||||
{
|
|
||||||
op = (OPCODE)*(ucell_t *)cip;
|
|
||||||
if ((unsigned)op >= OP_NUM_OPCODES)
|
|
||||||
{
|
|
||||||
AbortCompilation(co);
|
|
||||||
*err = SP_ERR_INVALID_INSTRUCTION;
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
cip += sizeof(cell_t);
|
|
||||||
op_c = OpAdvTable[op];
|
|
||||||
if (op_c >= 0)
|
|
||||||
{
|
|
||||||
cip += op_c;
|
|
||||||
} else if (op_c == -3) {
|
|
||||||
AbortCompilation(co);
|
|
||||||
*err = SP_ERR_INVALID_INSTRUCTION;
|
|
||||||
return NULL;
|
|
||||||
} else if (op_c == -1) {
|
|
||||||
switch (op)
|
|
||||||
{
|
|
||||||
case OP_CASETBL:
|
|
||||||
{
|
|
||||||
ucell_t num = *(ucell_t *)cip;
|
|
||||||
cip += sizeof(cell_t);
|
|
||||||
cip += ((2*num) + 1) * sizeof(cell_t);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
{
|
|
||||||
AbortCompilation(co);
|
|
||||||
*err = SP_ERR_INVALID_INSTRUCTION;
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*********************************************
|
/*********************************************
|
||||||
* SECOND PASS (medium load): writer.outbase is NULL, getting size only
|
* FIRST PASS (medium load): writer.outbase is NULL, getting size only
|
||||||
* THIRD PASS (heavy load!!): writer.outbase is valid and output is written
|
* SECOND PASS (heavy load!!): writer.outbase is valid and output is written
|
||||||
*********************************************/
|
*********************************************/
|
||||||
|
|
||||||
JitWriter writer;
|
JitWriter writer;
|
||||||
JitWriter *jit = &writer;
|
JitWriter *jit = &writer;
|
||||||
cell_t *endptr = (cell_t *)(end_cip);
|
cell_t *endptr = (cell_t *)(end_cip);
|
||||||
cell_t jitpos;
|
|
||||||
|
|
||||||
/* Initial code is written "blank,"
|
/* Initial code is written "blank,"
|
||||||
* so we can check the exact memory usage.
|
* so we can check the exact memory usage.
|
||||||
@ -1602,58 +1612,37 @@ sp_context_t *JITX86::CompileToContext(ICompilation *co, int *err)
|
|||||||
writer.outbase = NULL;
|
writer.outbase = NULL;
|
||||||
data->rebase = (jitcode_t)engine->BaseAlloc(plugin->pcode_size);
|
data->rebase = (jitcode_t)engine->BaseAlloc(plugin->pcode_size);
|
||||||
|
|
||||||
/* Jump back here for second pass */
|
/* We will jump back here for second pass */
|
||||||
jit_rewind:
|
jit_rewind:
|
||||||
/* Initialize pass vars */
|
/* Initialize pass vars */
|
||||||
writer.inptr = writer.inbase;
|
writer.inptr = writer.inbase;
|
||||||
data->jit_verify_addr_eax = 0;
|
data->jit_verify_addr_eax = 0;
|
||||||
data->jit_verify_addr_edx = 0;
|
data->jit_verify_addr_edx = 0;
|
||||||
|
|
||||||
/* Start writing the actual code */
|
/* Write the prologue of the JIT */
|
||||||
data->jit_return = Write_Execute_Function(jit);
|
data->jit_return = Write_Execute_Function(jit);
|
||||||
|
|
||||||
/* Write error checking routines that are jumped to */
|
/* Write the SYSREQ.N opcode if we need to */
|
||||||
if (!(data->inline_level & JIT_INLINE_ERRORCHECKS))
|
if (!(data->inline_level & JIT_INLINE_NATIVES))
|
||||||
{
|
{
|
||||||
jitpos = jit->get_outputpos();
|
data->jit_sysreq_n = jit->get_outputpos();
|
||||||
Write_Check_VerifyAddr(jit, REG_EAX);
|
WriteOp_Sysreq_N_Function(jit);
|
||||||
data->jit_verify_addr_eax = jitpos;
|
|
||||||
|
|
||||||
jitpos = jit->get_outputpos();
|
|
||||||
Write_Check_VerifyAddr(jit, REG_EDX);
|
|
||||||
data->jit_verify_addr_edx = jitpos;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Write error codes we need */
|
/* Write error checking routines that are called to */
|
||||||
|
if (!(data->inline_level & JIT_INLINE_ERRORCHECKS))
|
||||||
{
|
{
|
||||||
data->jit_error_divzero = jit->get_outputpos();
|
data->jit_verify_addr_eax = jit->get_outputpos();
|
||||||
Write_SetError(jit, true, SP_ERR_DIVIDE_BY_ZERO);
|
Write_Check_VerifyAddr(jit, REG_EAX);
|
||||||
|
|
||||||
data->jit_error_stacklow = jit->get_outputpos();
|
data->jit_verify_addr_edx = jit->get_outputpos();
|
||||||
Write_SetError(jit, true, SP_ERR_STACKLOW);
|
Write_Check_VerifyAddr(jit, REG_EDX);
|
||||||
|
|
||||||
data->jit_error_stackmin = jit->get_outputpos();
|
|
||||||
Write_SetError(jit, true, SP_ERR_STACKMIN);
|
|
||||||
|
|
||||||
data->jit_error_bounds = jit->get_outputpos();
|
|
||||||
Write_SetError(jit, true, SP_ERR_ARRAY_BOUNDS);
|
|
||||||
|
|
||||||
data->jit_error_memaccess = jit->get_outputpos();
|
|
||||||
Write_SetError(jit, true, SP_ERR_MEMACCESS);
|
|
||||||
|
|
||||||
data->jit_error_heaplow = jit->get_outputpos();
|
|
||||||
Write_SetError(jit, true, SP_ERR_HEAPLOW);
|
|
||||||
|
|
||||||
data->jit_error_heapmin = jit->get_outputpos();
|
|
||||||
Write_SetError(jit, true, SP_ERR_HEAPMIN);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Actual code generation! */
|
/* Actual code generation! */
|
||||||
if (writer.outbase == NULL)
|
if (writer.outbase == NULL)
|
||||||
{
|
{
|
||||||
/*******
|
/* First Pass - find codesize and resolve relocation */
|
||||||
* SECOND PASS - get opcode sizes+info
|
|
||||||
*******/
|
|
||||||
jitoffs_t pcode_offs;
|
jitoffs_t pcode_offs;
|
||||||
jitoffs_t native_offs;
|
jitoffs_t native_offs;
|
||||||
|
|
||||||
@ -1673,7 +1662,16 @@ jit_rewind:
|
|||||||
{
|
{
|
||||||
#include "opcode_switch.inc"
|
#include "opcode_switch.inc"
|
||||||
}
|
}
|
||||||
|
/* Check for errors. This should only happen in the first pass. */
|
||||||
|
if (data->error_set != SP_ERR_NONE)
|
||||||
|
{
|
||||||
|
*err = data->error_set;
|
||||||
|
AbortCompilation(co);
|
||||||
|
return NULL;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
/* Write these last because error jumps should be unpredicted, and thus forward */
|
||||||
|
WriteErrorRoutines(data, jit);
|
||||||
|
|
||||||
/* the total codesize is now known! */
|
/* the total codesize is now known! */
|
||||||
uint32_t mem = writer.get_outputpos();
|
uint32_t mem = writer.get_outputpos();
|
||||||
@ -1693,6 +1691,8 @@ jit_rewind:
|
|||||||
#include "opcode_switch.inc"
|
#include "opcode_switch.inc"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
/* Write these last because error jumps should be unpredicted, and thus forward */
|
||||||
|
WriteErrorRoutines(data, jit);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*************
|
/*************
|
||||||
@ -1810,8 +1810,7 @@ jit_rewind:
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* clean up relocation+compilation memory */
|
/* clean up relocation+compilation memory */
|
||||||
engine->BaseFree(data->rebase);
|
AbortCompilation(co);
|
||||||
delete data;
|
|
||||||
|
|
||||||
*err = SP_ERR_NONE;
|
*err = SP_ERR_NONE;
|
||||||
|
|
||||||
@ -1848,13 +1847,18 @@ ICompilation *JITX86::StartCompilation(sp_plugin_t *plugin)
|
|||||||
CompData *data = new CompData;
|
CompData *data = new CompData;
|
||||||
|
|
||||||
data->plugin = plugin;
|
data->plugin = plugin;
|
||||||
data->inline_level = JIT_INLINE_ERRORCHECKS;
|
data->inline_level = JIT_INLINE_ERRORCHECKS|JIT_INLINE_NATIVES;
|
||||||
|
data->error_set = SP_ERR_NONE;
|
||||||
|
|
||||||
return data;
|
return data;
|
||||||
}
|
}
|
||||||
|
|
||||||
void JITX86::AbortCompilation(ICompilation *co)
|
void JITX86::AbortCompilation(ICompilation *co)
|
||||||
{
|
{
|
||||||
|
if (((CompData *)co)->rebase)
|
||||||
|
{
|
||||||
|
engine->BaseFree(((CompData *)co)->rebase);
|
||||||
|
}
|
||||||
delete (CompData *)co;
|
delete (CompData *)co;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -33,15 +33,15 @@ public:
|
|||||||
jitoffs_t jit_error_memaccess;
|
jitoffs_t jit_error_memaccess;
|
||||||
jitoffs_t jit_error_heaplow;
|
jitoffs_t jit_error_heaplow;
|
||||||
jitoffs_t jit_error_heapmin;
|
jitoffs_t jit_error_heapmin;
|
||||||
|
jitoffs_t jit_extern_error;
|
||||||
uint32_t codesize;
|
uint32_t codesize;
|
||||||
int inline_level;
|
int inline_level;
|
||||||
|
int error_set;
|
||||||
bool debug;
|
bool debug;
|
||||||
};
|
};
|
||||||
|
|
||||||
class JITX86 : public IVirtualMachine
|
class JITX86 : public IVirtualMachine
|
||||||
{
|
{
|
||||||
public:
|
|
||||||
JITX86();
|
|
||||||
public:
|
public:
|
||||||
const char *GetVMName();
|
const char *GetVMName();
|
||||||
ICompilation *StartCompilation(sp_plugin_t *plugin);
|
ICompilation *StartCompilation(sp_plugin_t *plugin);
|
||||||
|
@ -4,8 +4,6 @@
|
|||||||
#include "opcode_helpers.h"
|
#include "opcode_helpers.h"
|
||||||
#include "x86_macros.h"
|
#include "x86_macros.h"
|
||||||
|
|
||||||
int OpAdvTable[OP_NUM_OPCODES];
|
|
||||||
|
|
||||||
#define NUM_INFO_PARAMS 7
|
#define NUM_INFO_PARAMS 7
|
||||||
|
|
||||||
jitoffs_t Write_Execute_Function(JitWriter *jit)
|
jitoffs_t Write_Execute_Function(JitWriter *jit)
|
||||||
@ -165,16 +163,26 @@ void Write_BreakDebug(JitWriter *jit)
|
|||||||
IA32_Return(jit);
|
IA32_Return(jit);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Write_SetError(JitWriter *jit, bool always_inline, int error)
|
void Write_GetError(JitWriter *jit)
|
||||||
|
{
|
||||||
|
CompData *data = (CompData *)jit->data;
|
||||||
|
|
||||||
|
//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, err));
|
||||||
|
IA32_Jump_Imm32_Abs(jit, data->jit_return);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Write_SetError(JitWriter *jit, int error)
|
||||||
{
|
{
|
||||||
CompData *data = (CompData *)jit->data;
|
CompData *data = (CompData *)jit->data;
|
||||||
|
|
||||||
/* These are so small that we always inline them! */
|
|
||||||
//mov eax, <error>
|
//mov eax, <error>
|
||||||
//jmp [...jit_return]
|
//jmp [jit_return]
|
||||||
IA32_Mov_Reg_Imm32(jit, REG_EAX, error);
|
IA32_Mov_Reg_Imm32(jit, REG_EAX, error);
|
||||||
jitoffs_t jmp = IA32_Jump_Imm32(jit, 0);
|
IA32_Jump_Imm32_Abs(jit, data->jit_return);
|
||||||
IA32_Write_Jump32(jit, jmp, data->jit_return);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Write_Check_DivZero(JitWriter *jit, jit_uint8_t reg)
|
void Write_Check_DivZero(JitWriter *jit, jit_uint8_t reg)
|
||||||
@ -426,11 +434,18 @@ void WriteOp_Sysreq_N_Function(JitWriter *jit)
|
|||||||
|
|
||||||
/* finally, push the last parameter and make the call */
|
/* finally, push the last parameter and make the call */
|
||||||
//push eax ; context
|
//push eax ; context
|
||||||
//mov eax, [eax+context]
|
|
||||||
//call NativeCallback
|
//call NativeCallback
|
||||||
IA32_Push_Reg(jit, REG_EAX);
|
IA32_Push_Reg(jit, REG_EAX);
|
||||||
jitoffs_t call = IA32_Call_Imm32(jit, 0);
|
jitoffs_t call = IA32_Call_Imm32(jit, 0);
|
||||||
IA32_Write_Jump32(jit, call, (jitoffs_t)(char *)&NativeCallback);
|
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_Imm32_Disp8(jit, AMX_REG_TMP, offsetof(sp_context_t, err), 0);
|
||||||
|
IA32_Jump_Cond_Imm32_Abs(jit, CC_NZ, data->jit_extern_error);
|
||||||
|
|
||||||
/* restore what we damaged */
|
/* restore what we damaged */
|
||||||
//add esp, 4*3
|
//add esp, 4*3
|
||||||
@ -442,12 +457,6 @@ void WriteOp_Sysreq_N_Function(JitWriter *jit)
|
|||||||
IA32_Pop_Reg(jit, AMX_REG_ALT);
|
IA32_Pop_Reg(jit, AMX_REG_ALT);
|
||||||
IA32_Pop_Reg(jit, REG_ECX);
|
IA32_Pop_Reg(jit, REG_ECX);
|
||||||
|
|
||||||
//Note: always safe, we're in a call
|
|
||||||
//test eax, eax
|
|
||||||
//jne :error
|
|
||||||
IA32_Test_Rm_Reg(jit, REG_EAX, REG_EAX, MOD_REG);
|
|
||||||
IA32_Jump_Cond_Imm32_Abs(jit, CC_NE, data->jit_return);
|
|
||||||
|
|
||||||
/* pop the AMX stack. do not check the margins.
|
/* pop the AMX stack. do not check the margins.
|
||||||
* Note that this is not a true macro - we don't bother to
|
* Note that this is not a true macro - we don't bother to
|
||||||
* set ALT here because nothing will be using it.
|
* set ALT here because nothing will be using it.
|
||||||
@ -459,183 +468,3 @@ void WriteOp_Sysreq_N_Function(JitWriter *jit)
|
|||||||
IA32_Return(jit);
|
IA32_Return(jit);
|
||||||
}
|
}
|
||||||
|
|
||||||
JITX86::JITX86()
|
|
||||||
{
|
|
||||||
memset(OpAdvTable, -1, sizeof(OpAdvTable));
|
|
||||||
|
|
||||||
/* instructions with 5 parameters */
|
|
||||||
OpAdvTable[OP_PUSH5_C] = sizeof(cell_t)*5;
|
|
||||||
OpAdvTable[OP_PUSH5] = sizeof(cell_t)*5;
|
|
||||||
OpAdvTable[OP_PUSH5_S] = sizeof(cell_t)*5;
|
|
||||||
OpAdvTable[OP_PUSH5_ADR] = sizeof(cell_t)*5;
|
|
||||||
|
|
||||||
/* instructions with 4 parameters */
|
|
||||||
OpAdvTable[OP_PUSH4_C] = sizeof(cell_t)*4;
|
|
||||||
OpAdvTable[OP_PUSH4] = sizeof(cell_t)*4;
|
|
||||||
OpAdvTable[OP_PUSH4_S] = sizeof(cell_t)*4;
|
|
||||||
OpAdvTable[OP_PUSH4_ADR] = sizeof(cell_t)*4;
|
|
||||||
|
|
||||||
/* instructions with 3 parameters */
|
|
||||||
OpAdvTable[OP_PUSH3_C] = sizeof(cell_t)*3;
|
|
||||||
OpAdvTable[OP_PUSH3] = sizeof(cell_t)*3;
|
|
||||||
OpAdvTable[OP_PUSH3_S] = sizeof(cell_t)*3;
|
|
||||||
OpAdvTable[OP_PUSH3_ADR] = sizeof(cell_t)*3;
|
|
||||||
|
|
||||||
/* instructions with 2 parameters */
|
|
||||||
OpAdvTable[OP_PUSH2_C] = sizeof(cell_t)*2;
|
|
||||||
OpAdvTable[OP_PUSH2] = sizeof(cell_t)*2;
|
|
||||||
OpAdvTable[OP_PUSH2_S] = sizeof(cell_t)*2;
|
|
||||||
OpAdvTable[OP_PUSH2_ADR] = sizeof(cell_t)*2;
|
|
||||||
OpAdvTable[OP_LOAD_BOTH] = sizeof(cell_t)*2;
|
|
||||||
OpAdvTable[OP_LOAD_S_BOTH] = sizeof(cell_t)*2;
|
|
||||||
OpAdvTable[OP_CONST] = sizeof(cell_t)*2;
|
|
||||||
OpAdvTable[OP_CONST_S] = sizeof(cell_t)*2;
|
|
||||||
OpAdvTable[OP_SYSREQ_N] = sizeof(cell_t)*2;
|
|
||||||
|
|
||||||
/* instructions with 1 parameter */
|
|
||||||
OpAdvTable[OP_LOAD_PRI] = sizeof(cell_t);
|
|
||||||
OpAdvTable[OP_LOAD_ALT] = sizeof(cell_t);
|
|
||||||
OpAdvTable[OP_LOAD_S_PRI] = sizeof(cell_t);
|
|
||||||
OpAdvTable[OP_LOAD_S_ALT] = sizeof(cell_t);
|
|
||||||
OpAdvTable[OP_LREF_PRI] = sizeof(cell_t);
|
|
||||||
OpAdvTable[OP_LREF_ALT] = sizeof(cell_t);
|
|
||||||
OpAdvTable[OP_LREF_S_PRI] = sizeof(cell_t);
|
|
||||||
OpAdvTable[OP_LREF_S_ALT] = sizeof(cell_t);
|
|
||||||
OpAdvTable[OP_CONST_PRI] = sizeof(cell_t);
|
|
||||||
OpAdvTable[OP_CONST_ALT] = sizeof(cell_t);
|
|
||||||
OpAdvTable[OP_ADDR_PRI] = sizeof(cell_t);
|
|
||||||
OpAdvTable[OP_ADDR_ALT] = sizeof(cell_t);
|
|
||||||
OpAdvTable[OP_STOR_PRI] = sizeof(cell_t);
|
|
||||||
OpAdvTable[OP_STOR_ALT] = sizeof(cell_t);
|
|
||||||
OpAdvTable[OP_STOR_S_PRI] = sizeof(cell_t);
|
|
||||||
OpAdvTable[OP_STOR_S_ALT] = sizeof(cell_t);
|
|
||||||
OpAdvTable[OP_SREF_PRI] = sizeof(cell_t);
|
|
||||||
OpAdvTable[OP_SREF_ALT] = sizeof(cell_t);
|
|
||||||
OpAdvTable[OP_SREF_S_PRI] = sizeof(cell_t);
|
|
||||||
OpAdvTable[OP_SREF_S_ALT] = sizeof(cell_t);
|
|
||||||
OpAdvTable[OP_LIDX_B] = sizeof(cell_t);
|
|
||||||
OpAdvTable[OP_IDXADDR_B] = sizeof(cell_t);
|
|
||||||
OpAdvTable[OP_PUSH_C] = sizeof(cell_t);
|
|
||||||
OpAdvTable[OP_PUSH] = sizeof(cell_t);
|
|
||||||
OpAdvTable[OP_PUSH_S] = sizeof(cell_t);
|
|
||||||
OpAdvTable[OP_STACK] = sizeof(cell_t);
|
|
||||||
OpAdvTable[OP_HEAP] = sizeof(cell_t);
|
|
||||||
OpAdvTable[OP_SHL_C_PRI] = sizeof(cell_t);
|
|
||||||
OpAdvTable[OP_SHL_C_ALT] = sizeof(cell_t);
|
|
||||||
OpAdvTable[OP_SHR_C_PRI] = sizeof(cell_t);
|
|
||||||
OpAdvTable[OP_SHR_C_ALT] = sizeof(cell_t);
|
|
||||||
OpAdvTable[OP_ADD_C] = sizeof(cell_t);
|
|
||||||
OpAdvTable[OP_SMUL_C] = sizeof(cell_t);
|
|
||||||
OpAdvTable[OP_ZERO] = sizeof(cell_t);
|
|
||||||
OpAdvTable[OP_ZERO_S] = sizeof(cell_t);
|
|
||||||
OpAdvTable[OP_EQ_C_PRI] = sizeof(cell_t);
|
|
||||||
OpAdvTable[OP_EQ_C_ALT] = sizeof(cell_t);
|
|
||||||
OpAdvTable[OP_INC] = sizeof(cell_t);
|
|
||||||
OpAdvTable[OP_INC_S] = sizeof(cell_t);
|
|
||||||
OpAdvTable[OP_DEC] = sizeof(cell_t);
|
|
||||||
OpAdvTable[OP_DEC_S] = sizeof(cell_t);
|
|
||||||
OpAdvTable[OP_MOVS] = sizeof(cell_t);
|
|
||||||
OpAdvTable[OP_FILL] = sizeof(cell_t);
|
|
||||||
OpAdvTable[OP_HALT] = sizeof(cell_t);
|
|
||||||
OpAdvTable[OP_BOUNDS] = sizeof(cell_t);
|
|
||||||
OpAdvTable[OP_PUSH_ADR] = sizeof(cell_t);
|
|
||||||
OpAdvTable[OP_PUSH_HEAP_C] = sizeof(cell_t);
|
|
||||||
OpAdvTable[OP_SYSREQ_C] = sizeof(cell_t);
|
|
||||||
OpAdvTable[OP_CALL] = sizeof(cell_t);
|
|
||||||
OpAdvTable[OP_JUMP] = sizeof(cell_t);
|
|
||||||
OpAdvTable[OP_JZER] = sizeof(cell_t);
|
|
||||||
OpAdvTable[OP_JNZ] = sizeof(cell_t);
|
|
||||||
OpAdvTable[OP_JEQ] = sizeof(cell_t);
|
|
||||||
OpAdvTable[OP_JNEQ] = sizeof(cell_t);
|
|
||||||
OpAdvTable[OP_JSLESS] = sizeof(cell_t);
|
|
||||||
OpAdvTable[OP_JSLEQ] = sizeof(cell_t);
|
|
||||||
OpAdvTable[OP_JSGRTR] = sizeof(cell_t);
|
|
||||||
OpAdvTable[OP_JSGEQ] = sizeof(cell_t);
|
|
||||||
OpAdvTable[OP_SWITCH] = sizeof(cell_t);
|
|
||||||
|
|
||||||
/* instructions with 0 parameters */
|
|
||||||
OpAdvTable[OP_LOAD_I] = 0;
|
|
||||||
OpAdvTable[OP_STOR_I] = 0;
|
|
||||||
OpAdvTable[OP_LIDX] = 0;
|
|
||||||
OpAdvTable[OP_IDXADDR] = 0;
|
|
||||||
OpAdvTable[OP_MOVE_PRI] = 0;
|
|
||||||
OpAdvTable[OP_MOVE_ALT] = 0;
|
|
||||||
OpAdvTable[OP_XCHG] = 0;
|
|
||||||
OpAdvTable[OP_PUSH_PRI] = 0;
|
|
||||||
OpAdvTable[OP_PUSH_ALT] = 0;
|
|
||||||
OpAdvTable[OP_POP_PRI] = 0;
|
|
||||||
OpAdvTable[OP_POP_ALT] = 0;
|
|
||||||
OpAdvTable[OP_PROC] = 0;
|
|
||||||
OpAdvTable[OP_RET] = 0;
|
|
||||||
OpAdvTable[OP_RETN] = 0;
|
|
||||||
OpAdvTable[OP_CALL_PRI] = 0;
|
|
||||||
OpAdvTable[OP_SHL] = 0;
|
|
||||||
OpAdvTable[OP_SHR] = 0;
|
|
||||||
OpAdvTable[OP_SSHR] = 0;
|
|
||||||
OpAdvTable[OP_SMUL] = 0;
|
|
||||||
OpAdvTable[OP_SDIV] = 0;
|
|
||||||
OpAdvTable[OP_SDIV_ALT] = 0;
|
|
||||||
OpAdvTable[OP_ADD] = 0;
|
|
||||||
OpAdvTable[OP_SUB] = 0;
|
|
||||||
OpAdvTable[OP_SUB_ALT] = 0;
|
|
||||||
OpAdvTable[OP_AND] = 0;
|
|
||||||
OpAdvTable[OP_OR] = 0;
|
|
||||||
OpAdvTable[OP_XOR] = 0;
|
|
||||||
OpAdvTable[OP_NOT] = 0;
|
|
||||||
OpAdvTable[OP_NEG] = 0;
|
|
||||||
OpAdvTable[OP_INVERT] = 0;
|
|
||||||
OpAdvTable[OP_ZERO_PRI] = 0;
|
|
||||||
OpAdvTable[OP_ZERO_ALT] = 0;
|
|
||||||
OpAdvTable[OP_SIGN_PRI] = 0;
|
|
||||||
OpAdvTable[OP_SIGN_ALT] = 0;
|
|
||||||
OpAdvTable[OP_EQ] = 0;
|
|
||||||
OpAdvTable[OP_NEQ] = 0;
|
|
||||||
OpAdvTable[OP_SLESS] = 0;
|
|
||||||
OpAdvTable[OP_SLEQ] = 0;
|
|
||||||
OpAdvTable[OP_SGRTR] = 0;
|
|
||||||
OpAdvTable[OP_SGEQ] = 0;
|
|
||||||
OpAdvTable[OP_INC_PRI] = 0;
|
|
||||||
OpAdvTable[OP_INC_ALT] = 0;
|
|
||||||
OpAdvTable[OP_INC_I] = 0;
|
|
||||||
OpAdvTable[OP_DEC_PRI] = 0;
|
|
||||||
OpAdvTable[OP_DEC_ALT] = 0;
|
|
||||||
OpAdvTable[OP_DEC_I] = 0;
|
|
||||||
OpAdvTable[OP_JUMP_PRI] = 0;
|
|
||||||
OpAdvTable[OP_SWAP_PRI] = 0;
|
|
||||||
OpAdvTable[OP_SWAP_ALT] = 0;
|
|
||||||
OpAdvTable[OP_NOP] = 0;
|
|
||||||
OpAdvTable[OP_BREAK] = 0;
|
|
||||||
OpAdvTable[OP_HEAP_PRI] = 0;
|
|
||||||
OpAdvTable[OP_POP_HEAP_PRI] = 0;
|
|
||||||
OpAdvTable[OP_SYSREQ_PRI] = 0;
|
|
||||||
|
|
||||||
/* opcodes that are totally invalid */
|
|
||||||
/* :TODO: make an alternate table if USE_UNGEN_OPCODES is on? */
|
|
||||||
OpAdvTable[OP_FILE] = -3;
|
|
||||||
OpAdvTable[OP_SYMBOL] = -3;
|
|
||||||
OpAdvTable[OP_LINE] = -3;
|
|
||||||
OpAdvTable[OP_SRANGE] = -3;
|
|
||||||
OpAdvTable[OP_SYMTAG] = -3;
|
|
||||||
OpAdvTable[OP_SYSREQ_D] = -3;
|
|
||||||
OpAdvTable[OP_SYSREQ_ND] = -3;
|
|
||||||
OpAdvTable[OP_PUSH_R] = -3;
|
|
||||||
OpAdvTable[OP_LODB_I] = -3;
|
|
||||||
OpAdvTable[OP_STRB_I] = -3;
|
|
||||||
OpAdvTable[OP_LCTRL] = -3;
|
|
||||||
OpAdvTable[OP_SCTRL] = -3;
|
|
||||||
OpAdvTable[OP_ALIGN_PRI] = -3;
|
|
||||||
OpAdvTable[OP_ALIGN_ALT] = -3;
|
|
||||||
OpAdvTable[OP_JREL] = -3;
|
|
||||||
OpAdvTable[OP_CMPS] = -3;
|
|
||||||
OpAdvTable[OP_UMUL] = -3;
|
|
||||||
OpAdvTable[OP_UDIV] = -3;
|
|
||||||
OpAdvTable[OP_UDIV_ALT] = -3;
|
|
||||||
OpAdvTable[OP_LESS] = -3;
|
|
||||||
OpAdvTable[OP_LEQ] = -3;
|
|
||||||
OpAdvTable[OP_GRTR] = -3;
|
|
||||||
OpAdvTable[OP_GEQ] = -3;
|
|
||||||
OpAdvTable[OP_JLESS] = -3;
|
|
||||||
OpAdvTable[OP_JLEQ] = -3;
|
|
||||||
OpAdvTable[OP_JGRTR] = -3;
|
|
||||||
OpAdvTable[OP_JGEQ] = -3;
|
|
||||||
}
|
|
||||||
|
@ -18,8 +18,10 @@ void WriteOp_Sysreq_N_Function(JitWriter *jit);
|
|||||||
/**
|
/**
|
||||||
* Generates code to set an error state in the VM and return.
|
* Generates code to set an error state in the VM and return.
|
||||||
* This is used for generating the error set points in the VM.
|
* This is used for generating the error set points in the VM.
|
||||||
|
* GetError writes the error from the context. SetError hardcodes.
|
||||||
*/
|
*/
|
||||||
void Write_SetError(JitWriter *jit, bool always_inline, int error);
|
void Write_GetError(JitWriter *jit);
|
||||||
|
void Write_SetError(JitWriter *jit, int error);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Checks the stacks for min and low errors.
|
* Checks the stacks for min and low errors.
|
||||||
@ -210,7 +212,7 @@ typedef enum
|
|||||||
OP_SWAP_ALT, //~VERIFIED (swap.alt)
|
OP_SWAP_ALT, //~VERIFIED (swap.alt)
|
||||||
OP_PUSH_ADR, //VERIFIED
|
OP_PUSH_ADR, //VERIFIED
|
||||||
OP_NOP, //VERIFIED (lol)
|
OP_NOP, //VERIFIED (lol)
|
||||||
OP_SYSREQ_N,
|
OP_SYSREQ_N, //VERIFIED
|
||||||
OP_SYMTAG, // !GEN DEPRECATED
|
OP_SYMTAG, // !GEN DEPRECATED
|
||||||
OP_BREAK, //DONE
|
OP_BREAK, //DONE
|
||||||
OP_PUSH2_C, //~VERIFIED (push3.c)
|
OP_PUSH2_C, //~VERIFIED (push3.c)
|
||||||
@ -244,6 +246,4 @@ typedef enum
|
|||||||
OP_NUM_OPCODES
|
OP_NUM_OPCODES
|
||||||
} OPCODE;
|
} OPCODE;
|
||||||
|
|
||||||
extern int OpAdvTable[];
|
|
||||||
|
|
||||||
#endif //_INCLUDE_SOURCEPAWN_JIT_X86_OPCODE_INFO_H_
|
#endif //_INCLUDE_SOURCEPAWN_JIT_X86_OPCODE_INFO_H_
|
||||||
|
@ -643,10 +643,22 @@
|
|||||||
WriteOp_Call(jit);
|
WriteOp_Call(jit);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
case OP_SYSREQ_N:
|
||||||
|
{
|
||||||
|
if (data->inline_level & JIT_INLINE_NATIVES)
|
||||||
|
{
|
||||||
|
WriteOp_Sysreq_N(jit);
|
||||||
|
} else {
|
||||||
|
WriteOp_Sysreq_N_NoInline(jit);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
#if defined USE_UNGEN_OPCODES
|
#if defined USE_UNGEN_OPCODES
|
||||||
#include "ungen_opcode_switch.inc"
|
#include "ungen_opcode_switch.inc"
|
||||||
#endif
|
#endif
|
||||||
default:
|
default:
|
||||||
{
|
{
|
||||||
assert(0);
|
data->error_set = SP_ERR_INVALID_INSTRUCTION;
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
@ -899,6 +899,22 @@ inline void IA32_Write_Jump32(JitWriter *jit, jitoffs_t jmp, jitoffs_t target)
|
|||||||
jit->outptr = oldptr;
|
jit->outptr = oldptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Corrects a jump using an absolute offset, not a relative one.
|
||||||
|
*/
|
||||||
|
inline void IA32_Write_Jump32_Abs(JitWriter *jit, jitoffs_t jmp, void *target)
|
||||||
|
{
|
||||||
|
//save old ptr
|
||||||
|
jitcode_t oldptr = jit->outptr;
|
||||||
|
//get relative difference
|
||||||
|
long diff = ((long)target - ((long)jit->outbase + jmp + 4));
|
||||||
|
//overwrite old value
|
||||||
|
jit->outptr = jit->outbase + jmp;
|
||||||
|
jit->write_int32(diff);
|
||||||
|
//restore old ptr
|
||||||
|
jit->outptr = oldptr;
|
||||||
|
}
|
||||||
|
|
||||||
/* For writing and auto-calculating an absolute target */
|
/* For writing and auto-calculating an absolute target */
|
||||||
inline void IA32_Jump_Imm32_Abs(JitWriter *jit, jitoffs_t target)
|
inline void IA32_Jump_Imm32_Abs(JitWriter *jit, jitoffs_t target)
|
||||||
{
|
{
|
||||||
|
@ -12,6 +12,7 @@ using namespace SourcePawn;
|
|||||||
BaseContext::BaseContext(sp_context_t *_ctx)
|
BaseContext::BaseContext(sp_context_t *_ctx)
|
||||||
{
|
{
|
||||||
ctx = _ctx;
|
ctx = _ctx;
|
||||||
|
ctx->context = this;
|
||||||
}
|
}
|
||||||
|
|
||||||
IVirtualMachine *BaseContext::GetVirtualMachine()
|
IVirtualMachine *BaseContext::GetVirtualMachine()
|
||||||
|
Loading…
Reference in New Issue
Block a user