dvander@367: /** danderson@3977: * vim: set ts=2 sw=2 tw=99 et: ds@1330: * ============================================================================= dvander@2114: * SourceMod dvander@2114: * Copyright (C) 2004-2008 AlliedModders LLC. All rights reserved. ds@1330: * ============================================================================= dvander@367: * dvander@2114: * This program is free software; you can redistribute it and/or modify it under dvander@2114: * the terms of the GNU General Public License, version 3.0, as published by the dvander@2114: * Free Software Foundation. dvander@2114: * dvander@2114: * This program is distributed in the hope that it will be useful, but WITHOUT dvander@2114: * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS dvander@2114: * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more dvander@2114: * details. dvander@2114: * dvander@2114: * You should have received a copy of the GNU General Public License along with dvander@2114: * this program. If not, see . dvander@2114: * dvander@2114: * As a special exception, AlliedModders LLC gives you permission to link the dvander@2114: * code of this program (as well as its derivative works) to "Half-Life 2," the dvander@2114: * "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software dvander@2114: * by the Valve Corporation. You must obey the GNU General Public License in dvander@2114: * all respects for all other code used. Additionally, AlliedModders LLC grants dvander@2114: * this exception to all derivative works. AlliedModders LLC defines further dvander@2114: * exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007), dvander@2114: * or . dvander@367: * dvander@367: * Version: $Id$ dvander@367: */ dvander@367: dvander@157: #include dvander@157: #include dvander@157: #include dvander@157: #include "jit_x86.h" dvander@2193: #include "../sp_vm_engine.h" dvander@2193: #include "../engine2.h" dvander@2193: #include "../BaseRuntime.h" dvander@2193: #include "../sp_vm_basecontext.h" ds@1952: dvander@2226: using namespace Knight; dvander@157: dvander@157: #if defined USE_UNGEN_OPCODES dvander@157: #include "ungen_opcodes.h" dvander@157: #endif dvander@157: danderson@3977: #define __ masm. danderson@3977: dvander@2226: JITX86 g_Jit; dvander@2226: KeCodeCache *g_pCodeCache = NULL; dvander@2193: ISourcePawnEngine *engine = &g_engine1; dvander@2193: danderson@3977: static inline void * danderson@3977: LinkCode(AssemblerX86 &masm) dvander@157: { danderson@3977: if (masm.outOfMemory()) danderson@3977: return NULL; dvander@157: danderson@3977: void *code = Knight::KE_AllocCode(g_pCodeCache, masm.length()); danderson@3977: if (!code) danderson@3977: return NULL; dvander@157: danderson@3977: masm.emitToExecutableMemory(code); danderson@3977: return code; dvander@157: } dvander@157: danderson@3977: static inline ConditionCode danderson@3977: OpToCondition(OPCODE op) dvander@157: { danderson@3977: switch (op) { danderson@3977: case OP_EQ: danderson@3977: case OP_JEQ: danderson@3977: return equal; danderson@3977: case OP_NEQ: danderson@3977: case OP_JNEQ: danderson@3977: return not_equal; danderson@3977: case OP_SLESS: danderson@3977: case OP_JSLESS: danderson@3977: return less; danderson@3977: case OP_SLEQ: danderson@3977: case OP_JSLEQ: danderson@3977: return less_equal; danderson@3977: case OP_SGRTR: danderson@3977: case OP_JSGRTR: danderson@3977: return greater; danderson@3977: case OP_SGEQ: danderson@3977: case OP_JSGEQ: danderson@3977: return greater_equal; danderson@3977: default: danderson@3977: assert(false); danderson@3977: return negative; danderson@3977: } dvander@157: } dvander@157: danderson@3977: struct array_creation_t dvander@157: { danderson@3977: const cell_t *dim_list; /* Dimension sizes */ danderson@3977: cell_t dim_count; /* Number of dimensions */ danderson@3977: cell_t *data_offs; /* Current offset AFTER the indirection vectors (data) */ danderson@3977: cell_t *base; /* array base */ danderson@3977: }; dvander@157: danderson@3977: static cell_t danderson@3977: GenerateInnerArrayIndirectionVectors(array_creation_t *ar, int dim, cell_t cur_offs) dvander@157: { danderson@3977: cell_t write_offs = cur_offs; danderson@3977: cell_t *data_offs = ar->data_offs; dvander@157: danderson@3977: cur_offs += ar->dim_list[dim]; dvander@157: danderson@3977: // Dimension n-x where x > 2 will have sub-vectors. danderson@3977: // Otherwise, we just need to reference the data section. danderson@3977: if (ar->dim_count > 2 && dim < ar->dim_count - 2) { danderson@3977: // For each index at this dimension, write offstes to our sub-vectors. danderson@3977: // After we write one sub-vector, we generate its sub-vectors recursively. danderson@3977: // At the end, we're given the next offset we can use. danderson@3977: for (int i = 0; i < ar->dim_list[dim]; i++) { danderson@3977: ar->base[write_offs] = (cur_offs - write_offs) * sizeof(cell_t); danderson@3977: write_offs++; danderson@3977: cur_offs = GenerateInnerArrayIndirectionVectors(ar, dim + 1, cur_offs); danderson@3977: } danderson@3977: } else { danderson@3977: // In this section, there are no sub-vectors, we need to write offsets danderson@3977: // to the data. This is separate so the data stays in one big chunk. danderson@3977: // The data offset will increment by the size of the last dimension, danderson@3977: // because that is where the data is finally computed as. danderson@3977: for (int i = 0; i < ar->dim_list[dim]; i++) { danderson@3977: ar->base[write_offs] = (*data_offs - write_offs) * sizeof(cell_t); danderson@3977: write_offs++; danderson@3977: *data_offs = *data_offs + ar->dim_list[dim + 1]; danderson@3977: } danderson@3977: } dvander@157: danderson@3977: return cur_offs; dvander@157: } dvander@157: danderson@3977: static cell_t danderson@3977: calc_indirection(const array_creation_t *ar, cell_t dim) dvander@157: { danderson@3977: cell_t size = ar->dim_list[dim]; danderson@3977: if (dim < ar->dim_count - 2) danderson@3977: size += ar->dim_list[dim] * calc_indirection(ar, dim + 1); danderson@3977: return size; dvander@157: } dvander@157: danderson@3977: static void danderson@3977: GenerateArrayIndirectionVectors(cell_t *arraybase, cell_t dims[], cell_t _dimcount, bool autozero) dvander@157: { danderson@3977: array_creation_t ar; danderson@3977: cell_t data_offs; dvander@157: danderson@3977: /* Reverse the dimensions */ danderson@3977: cell_t dim_list[sDIMEN_MAX]; danderson@3977: int cur_dim = 0; danderson@3977: for (int i = _dimcount - 1; i >= 0; i--) danderson@3977: dim_list[cur_dim++] = dims[i]; danderson@3977: danderson@3977: ar.base = arraybase; danderson@3977: ar.dim_list = dim_list; danderson@3977: ar.dim_count = _dimcount; danderson@3977: ar.data_offs = &data_offs; dvander@157: danderson@3977: data_offs = calc_indirection(&ar, 0); danderson@3977: danderson@3977: GenerateInnerArrayIndirectionVectors(&ar, 0, 0); dvander@157: } dvander@157: danderson@3977: static int danderson@3977: JIT_VerifyLowBoundTracker(sp_context_t *ctx) dvander@157: { danderson@3977: tracker_t *trk = (tracker_t *)(ctx->vm[JITVARS_TRACKER]); danderson@3977: if (trk->pCur <= trk->pBase) danderson@3977: return SP_ERROR_TRACKER_BOUNDS; danderson@3977: return SP_ERROR_NONE; dvander@157: } dvander@157: danderson@3977: static int danderson@3977: JIT_VerifyOrAllocateTracker(sp_context_t *ctx) dvander@157: { danderson@3977: tracker_t *trk = (tracker_t *)(ctx->vm[JITVARS_TRACKER]); dvander@157: danderson@3977: if ((size_t)(trk->pCur - trk->pBase) >= trk->size) danderson@3977: return SP_ERROR_TRACKER_BOUNDS; dvander@157: danderson@3977: if (trk->pCur+1 - (trk->pBase + trk->size) == 0) { danderson@3977: size_t disp = trk->size - 1; danderson@3977: trk->size *= 2; danderson@3977: trk->pBase = (ucell_t *)realloc(trk->pBase, trk->size * sizeof(cell_t)); dvander@157: danderson@3977: if (!trk->pBase) danderson@3977: return SP_ERROR_TRACKER_BOUNDS; danderson@3977: danderson@3977: trk->pCur = trk->pBase + disp; danderson@3977: } danderson@3977: danderson@3977: return SP_ERROR_NONE; dvander@157: } dvander@157: danderson@3977: #if !defined NDEBUG danderson@3977: static const char * danderson@3977: GetFunctionName(const sp_plugin_t *plugin, uint32_t offs) dvander@157: { danderson@3977: if (!plugin->debug.unpacked) { danderson@3977: uint32_t max, iter; danderson@3977: sp_fdbg_symbol_t *sym; danderson@3977: sp_fdbg_arraydim_t *arr; danderson@3977: uint8_t *cursor = (uint8_t *)(plugin->debug.symbols); dvander@157: danderson@3977: max = plugin->debug.syms_num; danderson@3977: for (iter = 0; iter < max; iter++) { danderson@3977: sym = (sp_fdbg_symbol_t *)cursor; dvander@157: danderson@3977: if (sym->ident == SP_SYM_FUNCTION && sym->codestart <= offs && sym->codeend > offs) danderson@3977: return plugin->debug.stringbase + sym->name; dvander@157: danderson@3977: if (sym->dimcount > 0) { danderson@3977: cursor += sizeof(sp_fdbg_symbol_t); danderson@3977: arr = (sp_fdbg_arraydim_t *)cursor; danderson@3977: cursor += sizeof(sp_fdbg_arraydim_t) * sym->dimcount; danderson@3977: continue; danderson@3977: } dvander@157: danderson@3977: cursor += sizeof(sp_fdbg_symbol_t); danderson@3977: } danderson@3977: } else { danderson@3977: uint32_t max, iter; danderson@3977: sp_u_fdbg_symbol_t *sym; danderson@3977: sp_u_fdbg_arraydim_t *arr; danderson@3977: uint8_t *cursor = (uint8_t *)(plugin->debug.symbols); dvander@157: danderson@3977: max = plugin->debug.syms_num; danderson@3977: for (iter = 0; iter < max; iter++) { danderson@3977: sym = (sp_u_fdbg_symbol_t *)cursor; danderson@3977: danderson@3977: if (sym->ident == SP_SYM_FUNCTION && sym->codestart <= offs && sym->codeend > offs) danderson@3977: return plugin->debug.stringbase + sym->name; dvander@157: danderson@3977: if (sym->dimcount > 0) { danderson@3977: cursor += sizeof(sp_u_fdbg_symbol_t); danderson@3977: arr = (sp_u_fdbg_arraydim_t *)cursor; danderson@3977: cursor += sizeof(sp_u_fdbg_arraydim_t) * sym->dimcount; danderson@3977: continue; danderson@3977: } dvander@157: danderson@3977: cursor += sizeof(sp_u_fdbg_symbol_t); danderson@3977: } danderson@3977: } dvander@157: danderson@3977: return NULL; dvander@157: } danderson@3977: #endif dvander@157: danderson@3977: static int danderson@3977: CompileFromThunk(BaseRuntime *runtime, cell_t pcode_offs, void **addrp, char *pc) dvander@157: { danderson@3977: JitFunction *fn = runtime->GetJittedFunctionByOffset(pcode_offs); danderson@3977: if (!fn) { danderson@3977: int err; danderson@3977: fn = g_Jit.CompileFunction(runtime, pcode_offs, &err); danderson@3977: if (!fn) danderson@3977: return err; danderson@3977: } dvander@157: danderson@3977: #if !defined NDEBUG danderson@3977: g_engine1.GetDebugHook()->OnDebugSpew( danderson@3977: "Patching thunk to %s::%s\n", danderson@3977: runtime->plugin()->name, danderson@3977: GetFunctionName(runtime->plugin(), pcode_offs)); danderson@3977: #endif dvander@157: danderson@3977: *addrp = fn->GetEntryAddress(); dvander@157: danderson@3977: /* Right now, we always keep the code RWE */ danderson@3977: *(intptr_t *)(pc - 4) = intptr_t(fn->GetEntryAddress()) - intptr_t(pc); danderson@3977: return SP_ERROR_NONE; dvander@157: } dvander@157: danderson@3977: static cell_t danderson@3977: NativeCallback(sp_context_t *ctx, ucell_t native_idx, cell_t *params) dvander@157: { danderson@3977: sp_native_t *native; danderson@3977: cell_t save_sp = ctx->sp; danderson@3977: cell_t save_hp = ctx->hp; danderson@3977: sp_plugin_t *pl = (sp_plugin_t *)(ctx->vm[JITVARS_PLUGIN]); danderson@3977: danderson@3977: ctx->n_idx = native_idx; danderson@3977: danderson@3977: if (ctx->hp < (cell_t)pl->data_size) { danderson@3977: ctx->n_err = SP_ERROR_HEAPMIN; danderson@3977: return 0; danderson@3977: } danderson@3977: danderson@3977: if (ctx->hp + STACK_MARGIN > ctx->sp) { danderson@3977: ctx->n_err = SP_ERROR_STACKLOW; danderson@3977: return 0; danderson@3977: } danderson@3977: danderson@3977: if ((uint32_t)ctx->sp >= pl->mem_size) { danderson@3977: ctx->n_err = SP_ERROR_STACKMIN; danderson@3977: return 0; danderson@3977: } danderson@3977: danderson@3977: native = &pl->natives[native_idx]; danderson@3977: danderson@3977: if (native->status == SP_NATIVE_UNBOUND) { danderson@3977: ctx->n_err = SP_ERROR_INVALID_NATIVE; danderson@3977: return 0; danderson@3977: } danderson@3977: danderson@3977: cell_t result = native->pfn(GET_CONTEXT(ctx), params); danderson@3977: danderson@3977: if (ctx->n_err != SP_ERROR_NONE) danderson@3977: return result; danderson@3977: danderson@3977: if (save_sp != ctx->sp) { danderson@3977: ctx->n_err = SP_ERROR_STACKLEAK; danderson@3977: return result; danderson@3977: } danderson@3977: if (save_hp != ctx->hp) { danderson@3977: ctx->n_err = SP_ERROR_HEAPLEAK; danderson@3977: return result; danderson@3977: } danderson@3977: danderson@3977: return result; dvander@157: } dvander@157: danderson@3977: Compiler::Compiler(BaseRuntime *rt, cell_t pcode_offs) danderson@3977: : rt_(rt), danderson@3977: plugin_(rt->plugin()), danderson@3977: error_(SP_ERROR_NONE), danderson@3977: pcode_start_(pcode_offs), danderson@3977: code_start_(reinterpret_cast(plugin_->pcode + pcode_start_)), danderson@3977: cip_(code_start_), danderson@3977: code_end_(reinterpret_cast(plugin_->pcode + plugin_->pcode_size)) dvander@157: { danderson@3977: size_t nmaxops = plugin_->pcode_size / sizeof(cell_t) + 1; danderson@3977: jump_map_ = new Label[nmaxops]; dvander@157: } dvander@157: danderson@3977: Compiler::~Compiler() dvander@157: { danderson@3977: delete [] jump_map_; dvander@157: } dvander@157: danderson@3977: JitFunction * danderson@3977: Compiler::emit(int *errp) dvander@157: { danderson@3977: if (cip_ >= code_end_ || *cip_ != OP_PROC) { danderson@3977: *errp = SP_ERROR_INVALID_INSTRUCTION; danderson@3977: return NULL; danderson@3977: } danderson@3977: danderson@3977: #if !defined NDEBUG danderson@3977: g_engine1.GetDebugHook()->OnDebugSpew( danderson@3977: "Compiling function %s::%s\n", danderson@3977: plugin_->name, danderson@3977: GetFunctionName(plugin_, pcode_start_)); dvander@157: danderson@3977: SpewOpcode(plugin_, code_start_, cip_); danderson@3977: #endif danderson@3977: danderson@3977: cell_t *codeseg = reinterpret_cast(plugin_->pcode); danderson@3977: danderson@3977: cip_++; danderson@3977: if (!emitOp(OP_PROC)) { danderson@3977: *errp = (error_ == SP_ERROR_NONE) ? SP_ERROR_OUT_OF_MEMORY : error_; danderson@3977: return NULL; danderson@3977: } dvander@157: danderson@3977: while (cip_ < code_end_) { danderson@3977: // If we reach the end of this function, or the beginning of a new danderson@3977: // procedure, then stop. danderson@3977: if (*cip_ == OP_PROC || *cip_ == OP_ENDPROC) danderson@3977: break; dvander@157: danderson@3977: #if !defined NDEBUG danderson@3977: SpewOpcode(plugin_, code_start_, cip_); danderson@3977: #endif danderson@3977: danderson@3977: // We assume every instruction is a jump target, so before emitting danderson@3977: // an opcode, we bind its corresponding label. danderson@3977: __ bind(&jump_map_[cip_ - codeseg]); dvander@157: danderson@3977: OPCODE op = (OPCODE)readCell(); danderson@3977: if (!emitOp(op) || error_ != SP_ERROR_NONE) { danderson@3977: *errp = (error_ == SP_ERROR_NONE) ? SP_ERROR_OUT_OF_MEMORY : error_; danderson@3977: return NULL; danderson@3977: } danderson@3977: } dvander@1945: danderson@3977: emitCallThunks(); danderson@3977: emitErrorPaths(); dvander@1945: danderson@3977: void *code = LinkCode(masm); danderson@3977: if (!code) { danderson@3977: *errp = SP_ERROR_OUT_OF_MEMORY; danderson@3977: return NULL; danderson@3977: } danderson@3977: danderson@3977: return new JitFunction(code, pcode_start_); dvander@1945: } dvander@1945: danderson@3977: bool danderson@3977: Compiler::emitOp(OPCODE op) dvander@157: { danderson@3977: switch (op) { danderson@3977: case OP_MOVE_PRI: danderson@3977: __ movl(pri, alt); danderson@3977: break; danderson@3977: danderson@3977: case OP_MOVE_ALT: danderson@3977: __ movl(alt, pri); danderson@3977: break; danderson@3977: danderson@3977: case OP_XCHG: danderson@3977: __ xchgl(pri, alt); danderson@3977: break; danderson@3977: danderson@3977: case OP_ZERO: danderson@3977: { danderson@3977: cell_t offset = readCell(); danderson@3977: __ movl(Operand(dat, offset), 0); danderson@3977: break; danderson@3977: } danderson@3977: danderson@3977: case OP_ZERO_S: danderson@3977: { danderson@3977: cell_t offset = readCell(); danderson@3977: __ movl(Operand(frm, offset), 0); danderson@3977: break; danderson@3977: } danderson@3977: danderson@3977: case OP_PUSH_PRI: danderson@3977: case OP_PUSH_ALT: danderson@3977: { danderson@3977: Register reg = (op == OP_PUSH_PRI) ? pri : alt; danderson@3977: __ movl(Operand(stk, -4), reg); danderson@3977: __ subl(stk, 4); danderson@3977: break; danderson@3977: } danderson@3977: danderson@3977: case OP_PUSH_C: danderson@3977: case OP_PUSH2_C: danderson@3977: case OP_PUSH3_C: danderson@3977: case OP_PUSH4_C: danderson@3977: case OP_PUSH5_C: danderson@3977: { danderson@3977: int n = 1; danderson@3977: if (op >= OP_PUSH2_C) danderson@3977: n = ((op - OP_PUSH2_C) / 4) + 2; dvander@157: danderson@3977: int i = 1; danderson@3977: do { danderson@3977: cell_t val = readCell(); danderson@3977: __ movl(Operand(stk, -(4 * i)), val); danderson@3977: } while (i++ < n); danderson@3977: __ subl(stk, 4 * n); danderson@3977: break; danderson@3977: } danderson@3977: danderson@3977: case OP_PUSH_ADR: danderson@3977: case OP_PUSH2_ADR: danderson@3977: case OP_PUSH3_ADR: danderson@3977: case OP_PUSH4_ADR: danderson@3977: case OP_PUSH5_ADR: danderson@3977: { danderson@3977: int n = 1; danderson@3977: if (op >= OP_PUSH2_ADR) danderson@3977: n = ((op - OP_PUSH2_ADR) / 4) + 2; danderson@3977: danderson@3977: int i = 1; dvander@157: danderson@3977: // We temporarily relocate FRM to be a local address instead of an danderson@3977: // absolute address. danderson@3977: __ subl(frm, dat); danderson@3977: do { danderson@3977: cell_t offset = readCell(); danderson@3977: __ lea(tmp, Operand(frm, offset)); danderson@3977: __ movl(Operand(stk, -(4 * i)), tmp); danderson@3977: } while (i++ < n); danderson@3977: __ subl(stk, 4 * n); danderson@3977: __ addl(frm, dat); danderson@3977: break; danderson@3977: } danderson@3977: danderson@3977: case OP_PUSH_S: danderson@3977: case OP_PUSH2_S: danderson@3977: case OP_PUSH3_S: danderson@3977: case OP_PUSH4_S: danderson@3977: case OP_PUSH5_S: danderson@3977: { danderson@3977: int n = 1; danderson@3977: if (op >= OP_PUSH2_S) danderson@3977: n = ((op - OP_PUSH2_S) / 4) + 2; danderson@3977: danderson@3977: int i = 1; danderson@3977: do { danderson@3977: cell_t offset = readCell(); danderson@3977: __ movl(tmp, Operand(frm, offset)); danderson@3977: __ movl(Operand(stk, -(4 * i)), tmp); danderson@3977: } while (i++ < n); danderson@3977: __ subl(stk, 4 * n); danderson@3977: break; danderson@3977: } dvander@157: danderson@3977: case OP_PUSH: danderson@3977: case OP_PUSH2: danderson@3977: case OP_PUSH3: danderson@3977: case OP_PUSH4: danderson@3977: case OP_PUSH5: danderson@3977: { danderson@3977: int n = 1; danderson@3977: if (op >= OP_PUSH2) danderson@3977: n = ((op - OP_PUSH2) / 4) + 2; danderson@3977: danderson@3977: int i = 1; danderson@3977: do { danderson@3977: cell_t offset = readCell(); danderson@3977: __ movl(tmp, Operand(dat, offset)); danderson@3977: __ movl(Operand(stk, -(4 * i)), tmp); danderson@3977: } while (i++ < n); danderson@3977: __ subl(stk, 4 * n); danderson@3977: break; danderson@3977: } danderson@3977: danderson@3977: case OP_ZERO_PRI: danderson@3977: __ xorl(pri, pri); danderson@3977: break; dvander@157: danderson@3977: case OP_ZERO_ALT: danderson@3977: __ xorl(alt, alt); danderson@3977: break; danderson@3977: danderson@3977: case OP_ADD: danderson@3977: __ addl(pri, alt); danderson@3977: break; danderson@3977: danderson@3977: case OP_SUB: danderson@3977: __ subl(pri, alt); danderson@3977: break; danderson@3977: danderson@3977: case OP_SUB_ALT: danderson@3977: __ movl(tmp, alt); danderson@3977: __ subl(tmp, pri); danderson@3977: __ movl(pri, tmp); danderson@3977: break; danderson@3977: danderson@3977: case OP_PROC: danderson@3977: // Push the old frame onto the stack. danderson@3977: __ movl(tmp, Operand(info, AMX_INFO_FRAME)); danderson@3977: __ movl(Operand(stk, -4), tmp); danderson@3977: __ subl(stk, 8); // extra unused slot for non-existant CIP danderson@3977: danderson@3977: // Get and store the new frame. danderson@3977: __ movl(tmp, stk); danderson@3977: __ movl(frm, stk); danderson@3977: __ subl(tmp, dat); danderson@3977: __ movl(Operand(info, AMX_INFO_FRAME), tmp); danderson@3977: break; dvander@157: danderson@3977: case OP_IDXADDR_B: danderson@3977: { danderson@3977: cell_t val = readCell(); danderson@3977: __ shll(pri, val); danderson@3977: __ addl(pri, alt); danderson@3977: break; danderson@3977: } danderson@3977: danderson@3977: case OP_SHL: danderson@3977: __ movl(ecx, alt); danderson@3977: __ shll_cl(pri); danderson@3977: break; danderson@3977: danderson@3977: case OP_SHR: danderson@3977: __ movl(ecx, alt); danderson@3977: __ shrl_cl(pri); danderson@3977: break; danderson@3977: danderson@3977: case OP_SSHR: danderson@3977: __ movl(ecx, alt); danderson@3977: __ sarl_cl(pri); danderson@3977: break; dvander@157: danderson@3977: case OP_SHL_C_PRI: danderson@3977: case OP_SHL_C_ALT: danderson@3977: { danderson@3977: Register reg = (op == OP_SHL_C_PRI) ? pri : alt; danderson@3977: cell_t val = readCell(); danderson@3977: __ shll(reg, val); danderson@3977: break; danderson@3977: } danderson@3977: danderson@3977: case OP_SHR_C_PRI: danderson@3977: case OP_SHR_C_ALT: danderson@3977: { danderson@3977: Register reg = (op == OP_SHR_C_PRI) ? pri : alt; danderson@3977: cell_t val = readCell(); danderson@3977: __ shrl(reg, val); danderson@3977: break; danderson@3977: } danderson@3977: danderson@3977: case OP_SMUL: danderson@3977: __ imull(pri, alt); danderson@3977: break; danderson@3977: danderson@3977: case OP_NOT: danderson@3977: __ testl(eax, eax); danderson@3977: __ movl(eax, 0); danderson@3977: __ set(zero, r8_al); danderson@3977: break; danderson@3977: danderson@3977: case OP_NEG: danderson@3977: __ negl(eax); danderson@3977: break; danderson@3977: danderson@3977: case OP_XOR: danderson@3977: __ xorl(pri, alt); danderson@3977: break; dvander@157: danderson@3977: case OP_OR: danderson@3977: __ orl(pri, alt); danderson@3977: break; danderson@3977: danderson@3977: case OP_AND: danderson@3977: __ andl(pri, alt); danderson@3977: break; danderson@3977: danderson@3977: case OP_INVERT: danderson@3977: __ notl(pri); danderson@3977: break; danderson@3977: danderson@3977: case OP_ADD_C: danderson@3977: { danderson@3977: cell_t val = readCell(); danderson@3977: __ addl(pri, val); danderson@3977: break; danderson@3977: } danderson@3977: danderson@3977: case OP_SMUL_C: danderson@3977: { danderson@3977: cell_t val = readCell(); danderson@3977: __ imull(pri, pri, val); danderson@3977: break; danderson@3977: } danderson@3977: danderson@3977: case OP_EQ: danderson@3977: case OP_NEQ: danderson@3977: case OP_SLESS: danderson@3977: case OP_SLEQ: danderson@3977: case OP_SGRTR: danderson@3977: case OP_SGEQ: danderson@3977: { danderson@3977: ConditionCode cc = OpToCondition(op); danderson@3977: __ cmpl(pri, alt); danderson@3977: __ movl(pri, 0); danderson@3977: __ set(cc, r8_al); danderson@3977: break; danderson@3977: } danderson@3977: danderson@3977: case OP_EQ_C_PRI: danderson@3977: case OP_EQ_C_ALT: danderson@3977: { danderson@3977: Register reg = (op == OP_EQ_C_PRI) ? pri : alt; danderson@3977: cell_t val = readCell(); danderson@3977: __ cmpl(reg, val); danderson@3977: __ movl(pri, 0); danderson@3977: __ set(equal, r8_al); danderson@3977: break; danderson@3977: } dvander@157: danderson@3977: case OP_INC_PRI: danderson@3977: case OP_INC_ALT: danderson@3977: { danderson@3977: Register reg = (OP_INC_PRI) ? pri : alt; danderson@3977: __ addl(reg, 1); danderson@3977: break; danderson@3977: } danderson@3977: danderson@3977: case OP_INC: danderson@3977: case OP_INC_S: danderson@3977: { danderson@3977: Register base = (op == OP_INC) ? dat : frm; danderson@3977: cell_t offset = readCell(); danderson@3977: __ addl(Operand(base, offset), 1); danderson@3977: break; danderson@3977: } danderson@3977: danderson@3977: case OP_INC_I: danderson@3977: __ addl(Operand(dat, pri, NoScale), 1); danderson@3977: break; danderson@3977: danderson@3977: case OP_DEC_PRI: danderson@3977: case OP_DEC_ALT: danderson@3977: { danderson@3977: Register reg = (op == OP_DEC_PRI) ? pri : alt; danderson@3977: __ subl(reg, 1); danderson@3977: break; danderson@3977: } danderson@3977: danderson@3977: case OP_DEC: danderson@3977: case OP_DEC_S: danderson@3977: { danderson@3977: Register base = (op == OP_DEC) ? dat : frm; danderson@3977: cell_t offset = readCell(); danderson@3977: __ subl(Operand(base, offset), 1); danderson@3977: break; danderson@3977: } danderson@3977: danderson@3977: case OP_DEC_I: danderson@3977: __ subl(Operand(dat, pri, NoScale), 1); danderson@3977: break; danderson@3977: danderson@3977: case OP_LOAD_PRI: danderson@3977: case OP_LOAD_ALT: danderson@3977: { danderson@3977: Register reg = (op == OP_LOAD_PRI) ? pri : alt; danderson@3977: cell_t offset = readCell(); danderson@3977: __ movl(reg, Operand(dat, offset)); danderson@3977: break; danderson@3977: } dvander@157: danderson@3977: case OP_LOAD_BOTH: danderson@3977: { danderson@3977: cell_t offs1 = readCell(); danderson@3977: cell_t offs2 = readCell(); danderson@3977: __ movl(pri, Operand(dat, offs1)); danderson@3977: __ movl(alt, Operand(dat, offs2)); danderson@3977: break; danderson@3977: } danderson@3977: danderson@3977: case OP_LOAD_S_PRI: danderson@3977: case OP_LOAD_S_ALT: danderson@3977: { danderson@3977: Register reg = (op == OP_LOAD_S_PRI) ? pri : alt; danderson@3977: cell_t offset = readCell(); danderson@3977: __ movl(reg, Operand(frm, offset)); danderson@3977: break; danderson@3977: } danderson@3977: danderson@3977: case OP_LOAD_S_BOTH: danderson@3977: { danderson@3977: cell_t offs1 = readCell(); danderson@3977: cell_t offs2 = readCell(); danderson@3977: __ movl(pri, Operand(frm, offs1)); danderson@3977: __ movl(alt, Operand(frm, offs2)); danderson@3977: break; danderson@3977: } danderson@3977: danderson@3977: case OP_LREF_S_PRI: danderson@3977: case OP_LREF_S_ALT: danderson@3977: { danderson@3977: Register reg = (op == OP_LREF_S_PRI) ? pri : alt; danderson@3977: cell_t offset = readCell(); danderson@3977: __ movl(reg, Operand(frm, offset)); danderson@3977: __ movl(reg, Operand(dat, reg, NoScale)); danderson@3977: break; danderson@3977: } danderson@3977: danderson@3977: case OP_CONST_PRI: danderson@3977: case OP_CONST_ALT: danderson@3977: { danderson@3977: Register reg = (op == OP_CONST_PRI) ? pri : alt; danderson@3977: cell_t val = readCell(); danderson@3977: __ movl(reg, val); danderson@3977: break; danderson@3977: } danderson@3977: danderson@3977: case OP_ADDR_PRI: danderson@3977: case OP_ADDR_ALT: danderson@3977: { danderson@3977: Register reg = (op == OP_ADDR_PRI) ? pri : alt; danderson@3977: cell_t offset = readCell(); danderson@3977: __ movl(reg, Operand(info, AMX_INFO_FRAME)); danderson@3977: __ addl(reg, offset); danderson@3977: break; danderson@3977: } danderson@3977: danderson@3977: case OP_STOR_PRI: danderson@3977: case OP_STOR_ALT: danderson@3977: { danderson@3977: Register reg = (op == OP_STOR_PRI) ? pri : alt; danderson@3977: cell_t offset = readCell(); danderson@3977: __ movl(Operand(dat, offset), reg); danderson@3977: break; danderson@3977: } danderson@3977: danderson@3977: case OP_STOR_S_PRI: danderson@3977: case OP_STOR_S_ALT: danderson@3977: { danderson@3977: Register reg = (op == OP_STOR_S_PRI) ? pri : alt; danderson@3977: cell_t offset = readCell(); danderson@3977: __ movl(Operand(frm, offset), reg); danderson@3977: break; danderson@3977: } danderson@3977: danderson@3977: case OP_IDXADDR: danderson@3977: __ lea(pri, Operand(alt, pri, ScaleFour)); danderson@3977: break; danderson@3977: danderson@3977: case OP_SREF_S_PRI: danderson@3977: case OP_SREF_S_ALT: danderson@3977: { danderson@3977: Register reg = (op == OP_SREF_S_PRI) ? pri : alt; danderson@3977: cell_t offset = readCell(); danderson@3977: __ movl(tmp, Operand(frm, offset)); danderson@3977: __ movl(Operand(dat, tmp, NoScale), reg); danderson@3977: break; danderson@3977: } danderson@3977: danderson@3977: case OP_POP_PRI: danderson@3977: case OP_POP_ALT: danderson@3977: { danderson@3977: Register reg = (op == OP_POP_PRI) ? pri : alt; danderson@3977: __ movl(reg, Operand(stk, 0)); danderson@3977: __ addl(stk, 4); danderson@3977: break; danderson@3977: } danderson@3977: danderson@3977: case OP_SWAP_PRI: danderson@3977: case OP_SWAP_ALT: danderson@3977: { danderson@3977: Register reg = (op == OP_SWAP_PRI) ? pri : alt; danderson@3977: __ movl(tmp, Operand(stk, 0)); danderson@3977: __ movl(Operand(stk, 0), reg); danderson@3977: __ movl(reg, tmp); danderson@3977: break; danderson@3977: } danderson@3977: danderson@3977: case OP_LIDX: danderson@3977: __ lea(pri, Operand(alt, pri, ScaleFour)); danderson@3977: __ movl(pri, Operand(dat, pri, NoScale)); danderson@3977: break; dvander@157: danderson@3977: case OP_LIDX_B: danderson@3977: { danderson@3977: cell_t val = readCell(); danderson@3977: if (val >= 0 && val <= 3) { danderson@3977: __ lea(pri, Operand(alt, pri, Scale(val))); danderson@3977: } else { danderson@3977: __ shll(pri, val); danderson@3977: __ addl(pri, alt); danderson@3977: } danderson@3977: emitCheckAddress(pri); danderson@3977: __ movl(pri, Operand(dat, pri, NoScale)); danderson@3977: break; danderson@3977: } danderson@3977: danderson@3977: case OP_CONST: danderson@3977: case OP_CONST_S: danderson@3977: { danderson@3977: Register base = (op == OP_CONST) ? dat : frm; danderson@3977: cell_t offset = readCell(); danderson@3977: cell_t val = readCell(); danderson@3977: __ movl(Operand(base, offset), val); danderson@3977: break; danderson@3977: } danderson@3977: danderson@3977: case OP_LOAD_I: danderson@3977: emitCheckAddress(pri); danderson@3977: __ movl(pri, Operand(dat, pri, NoScale)); danderson@3977: break; danderson@3977: danderson@3977: case OP_STOR_I: danderson@3977: emitCheckAddress(alt); danderson@3977: __ movl(Operand(dat, alt, NoScale), pri); danderson@3977: break; danderson@3977: danderson@3977: case OP_SDIV: danderson@3977: case OP_SDIV_ALT: danderson@3977: { danderson@3977: Register dividend = (op == OP_SDIV) ? pri : alt; danderson@3977: Register divisor = (op == OP_SDIV) ? alt : pri; danderson@3977: danderson@3977: // Guard against divide-by-zero. danderson@3977: __ testl(divisor, divisor); danderson@3977: __ j(zero, &error_divide_by_zero_); danderson@3977: danderson@3977: // A more subtle case; -INT_MIN / -1 yields an overflow exception. danderson@3977: Label ok; danderson@3977: __ cmpl(divisor, -1); danderson@3977: __ j(not_equal, &ok); danderson@3977: __ cmpl(dividend, 0x80000000); danderson@3977: __ j(equal, &error_integer_overflow_); danderson@3977: __ bind(&ok); danderson@3977: danderson@3977: // Now we can actually perform the divide. danderson@3977: __ movl(tmp, divisor); danderson@3977: if (op == OP_SDIV) danderson@3977: __ movl(edx, dividend); danderson@3977: else danderson@3977: __ movl(eax, dividend); danderson@3977: __ sarl(edx, 31); danderson@3977: __ idivl(tmp); danderson@3977: break; danderson@3977: } danderson@3977: danderson@3977: case OP_LODB_I: danderson@3977: { danderson@3977: cell_t val = readCell(); danderson@3977: emitCheckAddress(pri); danderson@3977: __ movl(pri, Operand(dat, pri, NoScale)); danderson@3977: if (val == 1) danderson@3977: __ andl(pri, 0xff); danderson@3977: else if (val == 2) danderson@3977: __ andl(pri, 0xffff); danderson@3977: break; danderson@3977: } danderson@3977: danderson@3977: case OP_STRB_I: danderson@3977: { danderson@3977: cell_t val = readCell(); danderson@3977: emitCheckAddress(alt); danderson@3977: if (val == 1) danderson@3977: __ movb(Operand(dat, alt, NoScale), pri); danderson@3977: else if (val == 2) danderson@3977: __ movw(Operand(dat, alt, NoScale), pri); danderson@3977: else if (val == 4) danderson@3977: __ movl(Operand(dat, alt, NoScale), pri); danderson@3977: break; danderson@3977: } danderson@3977: danderson@3977: case OP_RETN: danderson@3977: { danderson@3977: // Restore the old frame pointer. danderson@3977: __ movl(frm, Operand(stk, 4)); // get the old frm danderson@3977: __ addl(stk, 8); // pop stack danderson@3977: __ movl(Operand(info, AMX_INFO_FRAME), frm); // store back old frm danderson@3977: __ addl(frm, dat); // relocate danderson@3977: danderson@3977: // Remove parameters. danderson@3977: __ movl(tmp, Operand(stk, 0)); danderson@3977: __ lea(stk, Operand(stk, tmp, ScaleFour, 4)); danderson@3977: __ ret(); danderson@3977: break; danderson@3977: } danderson@3977: danderson@3977: case OP_MOVS: danderson@3977: { danderson@3977: cell_t val = readCell(); danderson@3977: unsigned dwords = val / 4; danderson@3977: unsigned bytes = val % 4; dvander@157: danderson@3977: __ cld(); danderson@3977: __ push(esi); danderson@3977: __ push(edi); danderson@3977: __ lea(esi, Operand(dat, pri, NoScale)); danderson@3977: __ lea(edi, Operand(dat, alt, NoScale)); danderson@3977: if (dwords) { danderson@3977: __ movl(ecx, dwords); danderson@3977: __ rep_movsd(); danderson@3977: } danderson@3977: if (bytes) { danderson@3977: __ movl(ecx, bytes); danderson@3977: __ rep_movsb(); danderson@3977: } danderson@3977: __ pop(edi); danderson@3977: __ pop(esi); danderson@3977: break; danderson@3977: } dvander@157: danderson@3977: case OP_FILL: danderson@3977: { danderson@3977: // eax/pri is used implicitly. danderson@3977: unsigned dwords = readCell() / 4; danderson@3977: __ push(edi); danderson@3977: __ lea(edi, Operand(dat, alt, NoScale)); danderson@3977: __ movl(ecx, dwords); danderson@3977: __ cld(); danderson@3977: __ rep_stosd(); danderson@3977: __ pop(edi); danderson@3977: break; danderson@3977: } danderson@3977: danderson@3977: case OP_STRADJUST_PRI: danderson@3977: __ addl(pri, 4); danderson@3977: __ sarl(pri, 2); danderson@3977: break; dvander@157: danderson@3977: case OP_FABS: danderson@3977: __ movl(pri, Operand(stk, 0)); danderson@3977: __ andl(pri, 0x7fffffff); danderson@3977: __ addl(stk, 4); danderson@3977: break; danderson@3977: danderson@3977: case OP_FLOAT: danderson@3979: if (MacroAssemblerX86::Features().sse2) { danderson@3979: __ cvtsi2ss(xmm0, Operand(edi, 0)); danderson@3979: __ movd(pri, xmm0); danderson@3979: } else { danderson@3979: __ fild32(Operand(edi, 0)); danderson@3979: __ subl(esp, 4); danderson@3979: __ fstp32(Operand(esp, 0)); danderson@3979: __ pop(pri); danderson@3979: } danderson@3977: __ addl(stk, 4); danderson@3977: break; danderson@3977: danderson@3977: case OP_FLOATADD: danderson@3977: case OP_FLOATSUB: danderson@3977: case OP_FLOATMUL: danderson@3977: case OP_FLOATDIV: danderson@3979: if (MacroAssemblerX86::Features().sse2) { danderson@3979: __ movss(xmm0, Operand(stk, 0)); danderson@3979: if (op == OP_FLOATADD) danderson@3979: __ addss(xmm0, Operand(stk, 4)); danderson@3979: else if (op == OP_FLOATSUB) danderson@3979: __ subss(xmm0, Operand(stk, 4)); danderson@3979: else if (op == OP_FLOATMUL) danderson@3979: __ mulss(xmm0, Operand(stk, 4)); danderson@3979: else if (op == OP_FLOATDIV) danderson@3979: __ divss(xmm0, Operand(stk, 4)); danderson@3979: __ movd(pri, xmm0); danderson@3979: } else { danderson@3979: __ subl(esp, 4); danderson@3979: __ fld32(Operand(stk, 0)); dvander@157: danderson@3979: if (op == OP_FLOATADD) danderson@3979: __ fadd32(Operand(stk, 4)); danderson@3979: else if (op == OP_FLOATSUB) danderson@3979: __ fsub32(Operand(stk, 4)); danderson@3979: else if (op == OP_FLOATMUL) danderson@3979: __ fmul32(Operand(stk, 4)); danderson@3979: else if (op == OP_FLOATDIV) danderson@3979: __ fdiv32(Operand(stk, 4)); danderson@3977: danderson@3979: __ fstp32(Operand(esp, 0)); danderson@3979: __ pop(pri); danderson@3979: } danderson@3977: __ addl(stk, 8); danderson@3977: break; dvander@157: danderson@3977: case OP_RND_TO_NEAREST: danderson@3977: { danderson@3979: if (MacroAssemblerX86::Features().sse) { danderson@3979: // Assume no one is touching MXCSR. danderson@3979: __ cvtss2si(pri, Operand(stk, 0)); danderson@3979: } else { danderson@3979: static float kRoundToNearest = 0.5f; danderson@3979: // From http://wurstcaptures.untergrund.net/assembler_tricks.html#fastfloorf danderson@3979: __ fld32(Operand(stk, 0)); danderson@3979: __ fadd32(st0, st0); danderson@3979: __ fadd32(Operand(ExternalAddress(&kRoundToNearest))); danderson@3979: __ subl(esp, 4); danderson@3979: __ fistp32(Operand(esp, 0)); danderson@3979: __ pop(pri); danderson@3979: __ sarl(pri, 1); danderson@3979: } danderson@3977: __ addl(stk, 4); danderson@3977: break; danderson@3977: } dvander@157: danderson@3977: case OP_RND_TO_CEIL: danderson@3977: { danderson@3977: static float kRoundToCeil = -0.5f; danderson@3977: // From http://wurstcaptures.untergrund.net/assembler_tricks.html#fastfloorf danderson@3979: __ fld32(Operand(stk, 0)); danderson@3977: __ fadd32(st0, st0); danderson@3977: __ fsubr32(Operand(ExternalAddress(&kRoundToCeil))); danderson@3977: __ subl(esp, 4); danderson@3977: __ fistp32(Operand(esp, 0)); danderson@3977: __ pop(pri); danderson@3977: __ sarl(pri, 1); danderson@3977: __ negl(pri); danderson@3977: __ addl(stk, 4); danderson@3977: break; danderson@3977: } dvander@157: danderson@3977: case OP_RND_TO_ZERO: danderson@3979: if (MacroAssemblerX86::Features().sse) { danderson@3979: __ cvttss2si(pri, Operand(stk, 0)); danderson@3979: } else { danderson@3979: __ fld32(Operand(stk, 0)); danderson@3979: __ subl(esp, 8); danderson@3979: __ fstcw(Operand(esp, 4)); danderson@3979: __ movl(Operand(esp, 0), 0xfff); danderson@3979: __ fldcw(Operand(esp, 0)); danderson@3979: __ fistp32(Operand(esp, 0)); danderson@3979: __ pop(pri); danderson@3979: __ fldcw(Operand(esp, 0)); danderson@3979: __ addl(esp, 4); danderson@3979: } danderson@3977: __ addl(stk, 4); danderson@3977: break; dvander@157: danderson@3977: case OP_RND_TO_FLOOR: danderson@3977: __ fld32(Operand(stk, 0)); danderson@3977: __ subl(esp, 8); danderson@3977: __ fstcw(Operand(esp, 4)); danderson@3977: __ movl(Operand(esp, 0), 0x7ff); danderson@3977: __ fldcw(Operand(esp, 0)); danderson@3977: __ fistp32(Operand(esp, 0)); danderson@3977: __ pop(eax); danderson@3977: __ fldcw(Operand(esp, 0)); danderson@3977: __ addl(esp, 4); danderson@3977: __ addl(stk, 4); danderson@3977: break; dvander@157: danderson@3977: case OP_FLOATCMP: danderson@3977: { danderson@3977: Label bl, ab, done; danderson@3979: if (MacroAssemblerX86::Features().sse) { danderson@3979: __ movss(xmm0, Operand(stk, 4)); danderson@3979: __ ucomiss(xmm0, Operand(stk, 0)); danderson@3979: } else { danderson@3979: __ fld32(Operand(stk, 0)); danderson@3979: __ fld32(Operand(stk, 4)); danderson@3979: __ fucomip(st1); danderson@3979: __ fstp(st0); danderson@3979: } danderson@3977: __ j(above, &ab); danderson@3977: __ j(below, &bl); danderson@3977: __ xorl(pri, pri); danderson@3977: __ jmp(&done); danderson@3977: __ bind(&ab); danderson@3977: __ movl(pri, -1); danderson@3977: __ jmp(&done); danderson@3977: __ bind(&bl); danderson@3977: __ movl(pri, 1); danderson@3977: __ bind(&done); danderson@3977: __ addl(stk, 8); danderson@3977: break; danderson@3977: } danderson@3977: danderson@3977: case OP_STACK: danderson@3977: { danderson@3977: cell_t amount = readCell(); danderson@3977: __ addl(stk, amount); danderson@3977: danderson@3977: if (amount > 0) { danderson@3977: // Check if the stack went beyond the stack top - usually a compiler error. danderson@3977: __ cmpl(stk, Operand(info, AMX_INFO_STACKTOP)); danderson@3977: __ j(not_below, &error_stack_min_); danderson@3977: } else { danderson@3977: // Check if the stack is going to collide with the heap. danderson@3977: __ movl(tmp, Operand(info, AMX_INFO_HEAP)); danderson@3977: __ lea(tmp, Operand(dat, ecx, NoScale, STACK_MARGIN)); danderson@3977: __ cmpl(stk, tmp); danderson@3977: __ j(below, &error_stack_low_); danderson@3977: } danderson@3977: break; danderson@3977: } dvander@157: danderson@3977: case OP_HEAP: danderson@3977: { danderson@3977: cell_t amount = readCell(); danderson@3977: __ movl(alt, Operand(info, AMX_INFO_HEAP)); danderson@3977: __ addl(Operand(info, AMX_INFO_HEAP), amount); danderson@3977: danderson@3977: if (amount > 0) { danderson@3977: __ cmpl(Operand(info, AMX_INFO_HEAP), plugin_->data_size); danderson@3977: __ j(below, &error_heap_min_); danderson@3977: } else { danderson@3977: __ movl(tmp, Operand(info, AMX_INFO_HEAP)); danderson@3977: __ lea(tmp, Operand(dat, ecx, NoScale, STACK_MARGIN)); danderson@3977: __ cmpl(tmp, stk); danderson@3977: __ j(above, &error_heap_low_); danderson@3977: } danderson@3977: break; danderson@3977: } danderson@3977: danderson@3977: case OP_JUMP: danderson@3977: { danderson@3977: Label *target = labelAt(readCell()); danderson@3977: if (!target) danderson@3977: return false; danderson@3977: __ jmp(target); danderson@3977: break; danderson@3977: } faluco@1166: danderson@3977: case OP_JZER: danderson@3977: case OP_JNZ: danderson@3977: { danderson@3977: ConditionCode cc = (op == OP_JZER) ? zero : not_zero; danderson@3977: Label *target = labelAt(readCell()); danderson@3977: if (!target) danderson@3977: return false; danderson@3977: __ testl(pri, pri); danderson@3977: __ j(cc, target); danderson@3977: break; danderson@3977: } danderson@3977: danderson@3977: case OP_JEQ: danderson@3977: case OP_JNEQ: danderson@3977: case OP_JSLESS: danderson@3977: case OP_JSLEQ: danderson@3977: case OP_JSGRTR: danderson@3977: case OP_JSGEQ: danderson@3977: { danderson@3977: Label *target = labelAt(readCell()); danderson@3977: if (!target) danderson@3977: return false; danderson@3977: ConditionCode cc = OpToCondition(op); danderson@3977: __ cmpl(pri, alt); danderson@3977: __ j(cc, target); danderson@3977: break; danderson@3977: } danderson@3977: danderson@3977: case OP_TRACKER_PUSH_C: danderson@3977: { danderson@3977: cell_t amount = readCell(); dvander@157: danderson@3977: // Save registers. danderson@3977: __ push(pri); danderson@3977: __ push(alt); danderson@3977: danderson@3977: // call JIT_VerifyorAllocateTracker danderson@3977: __ movl(eax, Operand(info, AMX_INFO_CONTEXT)); danderson@3977: __ push(eax); danderson@3977: __ call(ExternalAddress((void *)JIT_VerifyOrAllocateTracker)); danderson@3977: __ testl(eax, eax); danderson@3977: __ j(not_zero, &extern_error_); danderson@3977: __ pop(eax); danderson@3977: danderson@3977: // Push the value onto the stack and increment pCur. danderson@3977: __ movl(edx, Operand(eax, offsetof(sp_context_t, vm[JITVARS_TRACKER]))); danderson@3977: __ movl(ecx, Operand(edx, offsetof(tracker_t, pCur))); danderson@3977: __ addl(Operand(edx, offsetof(tracker_t, pCur)), 4); danderson@3977: __ movl(Operand(ecx, 0), amount * 4); danderson@3977: danderson@3977: __ pop(alt); danderson@3977: __ pop(pri); danderson@3977: break; danderson@3977: } danderson@3977: danderson@3977: case OP_TRACKER_POP_SETHEAP: danderson@3977: { danderson@3977: // Save registers. danderson@3977: __ push(pri); danderson@3977: __ push(alt); danderson@3977: danderson@3977: // Get the context pointer and call the sanity checker. danderson@3977: __ movl(eax, Operand(info, AMX_INFO_CONTEXT)); danderson@3977: __ push(eax); danderson@3977: __ call(ExternalAddress((void *)JIT_VerifyLowBoundTracker)); danderson@3977: __ testl(eax, eax); danderson@3977: __ j(not_zero, &extern_error_); danderson@3977: __ pop(eax); danderson@3977: danderson@3977: // Pop the value from the tracker stack and decrease the heap by it. danderson@3977: __ movl(edx, Operand(eax, offsetof(sp_context_t, vm[JITVARS_TRACKER]))); danderson@3977: __ movl(ecx, Operand(edx, offsetof(tracker_t, pCur))); danderson@3977: __ subl(ecx, 4); danderson@3977: __ movl(Operand(edx, offsetof(tracker_t, pCur)), eax); danderson@3977: __ movl(ecx, Operand(ecx, 0)); danderson@3977: __ subl(Operand(info, AMX_INFO_HEAP), ecx); danderson@3977: danderson@3977: // Check that we didn't underflow the heap. danderson@3977: __ cmpl(Operand(info, AMX_INFO_HEAP), plugin_->data_size); danderson@3977: __ j(below, &error_heap_min_); danderson@3977: danderson@3977: __ pop(alt); danderson@3977: __ pop(pri); danderson@3977: break; danderson@3977: } dvander@157: danderson@3977: case OP_BREAK: danderson@3977: { danderson@3977: cell_t cip = uintptr_t(cip_ - 1) - uintptr_t(plugin_->pcode); danderson@3977: __ movl(Operand(info, AMX_INFO_CIP), cip); danderson@3977: break; danderson@3977: } danderson@3977: danderson@3977: case OP_HALT: danderson@3977: __ align(16); danderson@3977: __ movl(tmp, Operand(info, AMX_INFO_RETVAL)); danderson@3977: __ movl(Operand(ecx, 0), pri); danderson@3977: __ movl(pri, readCell()); danderson@3977: __ jmp(&extern_error_); danderson@3977: break; danderson@3977: danderson@3977: case OP_BOUNDS: danderson@3977: { danderson@3977: cell_t value = readCell(); danderson@3977: __ cmpl(eax, value); danderson@3977: __ j(above, &error_bounds_); danderson@3977: break; danderson@3977: } danderson@3977: danderson@3977: case OP_GENARRAY: danderson@3977: case OP_GENARRAY_Z: danderson@3977: emitGenArray(op == OP_GENARRAY_Z); danderson@3977: break; dvander@157: danderson@3977: case OP_CALL: danderson@3977: if (!emitCall()) danderson@3977: return false; danderson@3977: break; danderson@3977: danderson@3977: case OP_SYSREQ_C: danderson@3977: case OP_SYSREQ_N: danderson@3977: if (!emitNativeCall(op)) danderson@3977: return false; danderson@3977: break; danderson@3977: danderson@3977: case OP_SWITCH: danderson@3977: if (!emitSwitch()) danderson@3977: return false; danderson@3977: break; dvander@157: danderson@3977: case OP_CASETBL: danderson@3977: { danderson@3977: size_t ncases = readCell(); danderson@3977: danderson@3977: // Two cells per case, and one extra cell for the default address. danderson@3977: cip_ += (ncases * 2) + 1; danderson@3977: break; danderson@3977: } danderson@3977: danderson@3977: case OP_NOP: danderson@3977: break; danderson@3977: danderson@3977: default: danderson@3977: error_ = SP_ERROR_INVALID_INSTRUCTION; danderson@3977: return false; danderson@3977: } danderson@3977: danderson@3977: return true; dvander@157: } dvander@157: danderson@3977: Label * danderson@3977: Compiler::labelAt(size_t offset) dvander@157: { danderson@3977: if (offset % 4 != 0 || danderson@3977: offset > plugin_->pcode_size || danderson@3977: offset <= pcode_start_) danderson@3977: { danderson@3977: // If the jump target is misaligned, or out of pcode bounds, or is an danderson@3977: // address out of the function bounds, we abort. Unfortunately we can't danderson@3977: // test beyond the end of the function since we don't have a precursor danderson@3977: // pass (yet). danderson@3977: error_ = SP_ERROR_INSTRUCTION_PARAM; danderson@3977: return NULL; danderson@3977: } dvander@157: danderson@3977: return &jump_map_[offset / sizeof(cell_t)]; dvander@157: } dvander@157: danderson@3977: static void danderson@3977: EmitTrackerPushReg(AssemblerX86 &masm, Register reg, Label *error) dvander@157: { danderson@3977: __ push(eax); danderson@3977: if (reg == ecx) danderson@3977: __ push(ecx); danderson@3977: __ push(edi); danderson@3977: __ movl(edi, reg); danderson@3977: __ shll(edi, 2); // Count in bytes, not cells. dvander@157: danderson@3977: // Get the context pointer, push it and call the check. danderson@3977: __ movl(eax, Operand(info, AMX_INFO_CONTEXT)); danderson@3977: __ push(eax); danderson@3977: __ call(ExternalAddress((void *)JIT_VerifyOrAllocateTracker)); dvander@237: danderson@3977: // Check for errors. danderson@3977: __ testl(eax, eax); danderson@3977: __ j(not_zero, error); danderson@3977: danderson@3977: __ pop(eax); dvander@157: danderson@3977: // Push the register into the stack and increment pCur. danderson@3977: __ movl(edx, Operand(eax, offsetof(sp_context_t, vm[JITVARS_TRACKER]))); danderson@3977: __ movl(eax, Operand(edx, offsetof(tracker_t, pCur))); danderson@3977: __ addl(Operand(edx, offsetof(tracker_t, pCur)), 4); danderson@3977: __ movl(Operand(eax, 0), edi); dvander@157: danderson@3977: // Restore pri, alt, stk. danderson@3977: __ pop(edi); danderson@3977: if (reg == ecx) danderson@3977: __ pop(ecx); danderson@3977: __ pop(eax); dvander@157: } dvander@157: danderson@3977: void danderson@3977: Compiler::emitCheckAddress(Register reg) faluco@831: { danderson@3977: // Check if we're in memory bounds. danderson@3977: __ cmpl(reg, plugin_->mem_size); danderson@3977: __ j(not_below, &error_memaccess_); faluco@831: danderson@3977: // Check if we're in the invalid region between hp and sp. danderson@3977: Label done; danderson@3977: __ cmpl(reg, Operand(info, AMX_INFO_HEAP)); danderson@3977: __ j(below, &done); danderson@3977: __ lea(tmp, Operand(dat, reg, NoScale)); danderson@3977: __ cmpl(tmp, stk); danderson@3977: __ j(below, &error_memaccess_); danderson@3977: __ bind(&done); faluco@831: } faluco@831: danderson@3977: void danderson@3977: Compiler::emitGenArray(bool autozero) faluco@831: { danderson@3977: cell_t val = readCell(); danderson@3977: if (val == 1) danderson@3977: { danderson@3977: // flat array; we can generate this without indirection tables. danderson@3977: // Note that we can overwrite ALT because technically STACK should be destroying ALT danderson@3977: __ movl(alt, Operand(info, AMX_INFO_HEAP)); danderson@3977: __ movl(tmp, Operand(stk, 0)); danderson@3977: __ movl(Operand(stk, 0), alt); // store base of the array into the stack. danderson@3977: __ lea(alt, Operand(alt, tmp, ScaleFour)); danderson@3977: __ movl(Operand(info, AMX_INFO_HEAP), alt); danderson@3977: __ addl(alt, dat); danderson@3977: __ cmpl(alt, stk); danderson@3977: __ j(not_below, &error_heap_low_); faluco@831: danderson@3977: EmitTrackerPushReg(masm, ecx, &extern_error_); faluco@831: danderson@3977: if (autozero) { danderson@3977: __ push(eax); danderson@3977: __ push(edi); danderson@3977: __ xorl(eax, eax); danderson@3977: __ movl(edi, Operand(stk, 0)); danderson@3977: __ addl(edi, dat); danderson@3977: __ cld(); danderson@3977: __ rep_stosd(); danderson@3977: __ pop(edi); danderson@3977: __ pop(eax); danderson@3977: } danderson@3977: } else { danderson@3977: __ movl(tmp, val); danderson@3977: __ movl(edx, autozero ? 1 : 0); danderson@3977: __ call(g_Jit.GetGenArrayIntrinsic()); danderson@3977: } faluco@831: } faluco@831: danderson@3977: bool danderson@3977: Compiler::emitCall() faluco@831: { danderson@3977: cell_t offset = readCell(); faluco@831: danderson@3977: // If this offset looks crappy, i.e. not aligned or out of bounds, we just danderson@3977: // abort. danderson@3977: if (offset % 4 != 0 || uint32_t(offset) >= plugin_->pcode_size) { danderson@3977: error_ = SP_ERROR_INSTRUCTION_PARAM; danderson@3977: return false; danderson@3977: } faluco@831: danderson@3977: // eax = context danderson@3977: // ecx = rp danderson@3977: __ movl(eax, Operand(info, AMX_INFO_CONTEXT)); danderson@3977: __ movl(ecx, Operand(eax, offsetof(sp_context_t, rp))); faluco@831: danderson@3977: // Check if the return stack is used up. danderson@3977: __ cmpl(ecx, SP_MAX_RETURN_STACK); danderson@3977: __ j(not_below, &error_stack_low_); danderson@3977: danderson@3977: // Add to the return stack. danderson@3977: uintptr_t cip = uintptr_t(cip_ - 2) - uintptr_t(plugin_->pcode); danderson@3977: __ movl(Operand(eax, ecx, ScaleFour, offsetof(sp_context_t, rstk_cips)), cip); faluco@831: danderson@3977: // Increment the return stack pointer. danderson@3977: __ addl(Operand(eax, offsetof(sp_context_t, rp)), 1); faluco@831: danderson@3977: // Store the CIP of the function we're about to call. danderson@3977: __ movl(Operand(info, AMX_INFO_CIP), offset); faluco@831: danderson@3977: JitFunction *fun = rt_->GetJittedFunctionByOffset(offset); danderson@3977: if (!fun) { danderson@3977: // Need to emit a delayed thunk. danderson@3977: CallThunk *thunk = new CallThunk(offset); danderson@3977: __ call(&thunk->call); danderson@3977: if (!thunks_.append(thunk)) danderson@3977: return false; danderson@3977: } else { danderson@3977: // Function is already emitted, we can do a direct call. danderson@3977: __ call(ExternalAddress(fun->GetEntryAddress())); danderson@3977: } faluco@831: danderson@3977: // Restore the last cip. danderson@3977: __ movl(Operand(info, AMX_INFO_CIP), cip); faluco@831: danderson@3977: // Mark us as leaving the last frame. danderson@3977: __ movl(tmp, Operand(info, AMX_INFO_CONTEXT)); danderson@3977: __ subl(Operand(tmp, offsetof(sp_context_t, rp)), 1); danderson@3977: return true; faluco@831: } faluco@831: danderson@3977: void danderson@3977: Compiler::emitCallThunks() faluco@831: { danderson@3977: for (size_t i = 0; i < thunks_.length(); i++) { danderson@3977: CallThunk *thunk = thunks_[i]; faluco@831: danderson@3977: Label error; danderson@3977: __ bind(&thunk->call); danderson@3977: // Huge hack - get the return address, since that is the call that we danderson@3977: // need to patch. danderson@3977: __ movl(eax, Operand(esp, 0)); faluco@831: danderson@3977: // Align the stack. Unfortunately this is a pretty careful dance based danderson@3977: // on the number of words we push (4 args, 1 saved esp), and someday danderson@3977: // we should look into automating this. danderson@3977: __ movl(edx, esp); danderson@3977: __ andl(esp, 0xfffffff0); danderson@3977: __ subl(esp, 12); danderson@3977: __ push(edx); faluco@831: danderson@3977: // Push arguments. danderson@3977: __ push(eax); danderson@3977: __ subl(esp, 4); danderson@3977: __ movl(Operand(esp, 0), esp); danderson@3977: __ push(thunk->pcode_offset); danderson@3977: __ push(intptr_t(rt_)); danderson@3977: __ call(ExternalAddress((void *)CompileFromThunk)); danderson@3977: __ movl(edx, Operand(esp, 8)); danderson@3977: __ movl(esp, Operand(esp, 16)); danderson@3977: __ testl(eax, eax); danderson@3977: __ j(not_zero, &error); danderson@3977: __ call(edx); danderson@3977: __ ret(); faluco@831: danderson@3977: __ bind(&error); danderson@3977: __ movl(Operand(info, AMX_INFO_CIP), thunk->pcode_offset); danderson@3977: __ jmp(g_Jit.GetUniversalReturn()); danderson@3977: } danderson@3977: } faluco@831: danderson@3977: cell_t danderson@3977: Compiler::readCell() danderson@3977: { danderson@3977: if (cip_ >= code_end_) { danderson@3977: error_= SP_ERROR_INVALID_INSTRUCTION; danderson@3977: return 0; danderson@3977: } danderson@3977: return *cip_++; faluco@831: } faluco@831: danderson@3977: bool danderson@3977: Compiler::emitNativeCall(OPCODE op) faluco@831: { danderson@3977: uint32_t native_index = readCell(); faluco@831: danderson@3977: if (native_index >= plugin_->num_natives) { danderson@3977: error_ = SP_ERROR_INSTRUCTION_PARAM; danderson@3977: return false; danderson@3977: } faluco@831: danderson@3977: uint32_t num_params; danderson@3977: if (op == OP_SYSREQ_N) { danderson@3977: num_params = readCell(); faluco@831: danderson@3977: // See if we can get a replacement opcode. If we can, then recursively danderson@3977: // call emitOp() to generate it. Note: it's important that we do this danderson@3977: // before generating any code for the SYSREQ.N. danderson@3977: unsigned replacement = rt_->GetNativeReplacement(native_index); danderson@3977: if (replacement != OP_NOP) danderson@3977: return emitOp((OPCODE)replacement); faluco@831: danderson@3977: // Store the number of parameters. danderson@3977: __ movl(Operand(stk, -4), num_params); danderson@3977: __ subl(stk, 4); danderson@3977: } faluco@831: danderson@3977: // Save registers. danderson@3977: __ push(edx); faluco@831: danderson@3977: // Align the stack to 16 bytes. danderson@3977: __ push(ebx); danderson@3977: __ movl(ebx, esp); danderson@3977: __ andl(esp, 0xfffffff0); danderson@3977: __ subl(esp, 4); faluco@831: danderson@3977: // Push the parameters for the C++ function. danderson@3977: __ push(stk); danderson@3977: __ push(native_index); faluco@831: danderson@3977: // Relocate all our absolute junk to be dat-relative, and store it all back danderson@3977: // into the context. danderson@3977: __ movl(eax, Operand(info, AMX_INFO_CONTEXT)); danderson@3977: __ movl(ecx, Operand(info, AMX_INFO_HEAP)); danderson@3977: __ movl(Operand(eax, offsetof(sp_context_t, hp)), ecx); danderson@3977: __ subl(stk, dat); danderson@3977: __ movl(Operand(eax, offsetof(sp_context_t, sp)), stk); danderson@3977: __ movl(ecx, Operand(info, AMX_INFO_FRAME)); danderson@3977: __ movl(Operand(eax, offsetof(sp_context_t, frm)), ecx); faluco@831: danderson@3977: // Push context and make the call. danderson@3977: __ push(eax); danderson@3977: __ call(ExternalAddress((void *)NativeCallback)); faluco@831: danderson@3977: // Check for errors. danderson@3977: __ movl(ecx, Operand(info, AMX_INFO_CONTEXT)); danderson@3977: __ movl(ecx, Operand(ecx, offsetof(sp_context_t, n_err))); danderson@3977: __ testl(ecx, ecx); danderson@3977: __ j(not_zero, &extern_error_); danderson@3977: danderson@3977: // Restore local state. danderson@3977: __ movl(esp, ebx); danderson@3977: __ pop(ebx); danderson@3977: __ addl(stk, dat); danderson@3977: __ pop(edx); dvander@2226: danderson@3977: if (op == OP_SYSREQ_N) { danderson@3977: // Pop the stack. Do not check the margins. danderson@3977: __ addl(stk, (num_params + 1) * sizeof(cell_t)); danderson@3977: } danderson@3977: return true; dvander@2226: } dvander@2226: danderson@3977: bool danderson@3977: Compiler::emitSwitch() dvander@2226: { danderson@3977: cell_t offset = readCell(); danderson@3977: if (!labelAt(offset)) danderson@3977: return false; danderson@3977: danderson@3977: cell_t *tbl = (cell_t *)((char *)plugin_->pcode + offset + sizeof(cell_t)); danderson@3977: danderson@3977: struct Entry { danderson@3977: cell_t val; danderson@3977: cell_t offset; danderson@3977: }; danderson@3977: danderson@3977: size_t ncases = *tbl++; danderson@3977: danderson@3977: Label *defaultCase = labelAt(*tbl); danderson@3977: if (!defaultCase) danderson@3977: return false; danderson@3977: danderson@3977: // Degenerate - 0 cases. danderson@3977: if (!ncases) { danderson@3977: __ jmp(defaultCase); danderson@3977: return true; danderson@3977: } danderson@3977: danderson@3977: Entry *cases = (Entry *)(tbl + 1); danderson@3977: danderson@3977: // Degenerate - 1 case. danderson@3977: if (ncases == 1) { danderson@3977: Label *maybe = labelAt(cases[0].offset); danderson@3977: if (!maybe) danderson@3977: return false; danderson@3977: __ cmpl(pri, cases[0].val); danderson@3977: __ j(equal, maybe); danderson@3977: __ jmp(defaultCase); danderson@3977: return true; danderson@3977: } danderson@3977: danderson@3977: // We have two or more cases, so let's generate a full switch. Decide danderson@3977: // whether we'll make an if chain, or a jump table, based on whether danderson@3977: // the numbers are strictly sequential. danderson@3977: bool sequential = true; danderson@3977: { danderson@3977: cell_t first = cases[0].val; danderson@3977: cell_t last = first; danderson@3977: for (size_t i = 1; i < ncases; i++) { danderson@3977: if (cases[i].val != ++last) { danderson@3977: sequential = false; danderson@3977: break; danderson@3977: } danderson@3977: } danderson@3977: } danderson@3977: danderson@3977: // First check whether the bounds are correct: if (a < LOW || a > HIGH); danderson@3977: // this check is valid whether or not we emit a sequential-optimized switch. danderson@3977: cell_t low = cases[0].val; danderson@3977: if (low != 0) { danderson@3977: // negate it so we'll get a lower bound of 0. danderson@3977: low = -low; danderson@3977: __ lea(tmp, Operand(pri, low)); danderson@3977: } else { danderson@3977: __ movl(tmp, pri); danderson@3977: } dvander@2226: danderson@3977: cell_t high = abs(cases[0].val - cases[ncases - 1].val); danderson@3977: __ cmpl(tmp, high); danderson@3977: __ j(above, defaultCase); dvander@2226: danderson@3977: if (sequential) { danderson@3977: // Optimized table version. The tomfoolery below is because we only have danderson@3977: // one free register... it seems unlikely pri or alt will be used given danderson@3977: // that we're at the end of a control-flow point, but we'll play it safe. danderson@3977: DataLabel table; danderson@3977: __ push(eax); danderson@3977: __ movl(eax, &table); danderson@3977: __ movl(ecx, Operand(eax, ecx, ScaleFour)); danderson@3977: __ pop(eax); danderson@3977: __ jmp(ecx); dvander@2226: danderson@3977: __ bind(&table); danderson@3977: for (size_t i = 0; i < ncases; i++) { danderson@3977: Label *label = labelAt(cases[i].offset); danderson@3977: if (!label) danderson@3977: return false; danderson@3977: __ emit_absolute_address(label); danderson@3977: } danderson@3977: } else { danderson@3977: // Slower version. Go through each case and generate a check. danderson@3977: for (size_t i = 0; i < ncases; i++) { danderson@3977: Label *label = labelAt(cases[i].offset); danderson@3977: if (!label) danderson@3977: return false; danderson@3977: __ cmpl(pri, cases[i].val); danderson@3977: __ j(equal, label); danderson@3977: } danderson@3977: __ jmp(defaultCase); danderson@3977: } danderson@3977: return true; danderson@3977: } dvander@2226: danderson@3977: void danderson@3977: Compiler::emitErrorPath(Label *dest, int code) danderson@3977: { danderson@3977: if (dest->used()) { danderson@3977: __ bind(dest); danderson@3977: __ movl(eax, code); danderson@3977: __ jmp(g_Jit.GetUniversalReturn()); danderson@3977: } danderson@3977: } dvander@2226: danderson@3977: void danderson@3977: Compiler::emitErrorPaths() danderson@3977: { danderson@3977: emitErrorPath(&error_divide_by_zero_, SP_ERROR_DIVIDE_BY_ZERO); danderson@3977: emitErrorPath(&error_stack_low_, SP_ERROR_STACKLOW); danderson@3977: emitErrorPath(&error_stack_min_, SP_ERROR_STACKMIN); danderson@3977: emitErrorPath(&error_bounds_, SP_ERROR_ARRAY_BOUNDS); danderson@3977: emitErrorPath(&error_memaccess_, SP_ERROR_MEMACCESS); danderson@3977: emitErrorPath(&error_heap_low_, SP_ERROR_HEAPLOW); danderson@3977: emitErrorPath(&error_heap_min_, SP_ERROR_HEAPMIN); danderson@3977: emitErrorPath(&error_integer_overflow_, SP_ERROR_INTEGER_OVERFLOW); dvander@2226: danderson@3977: if (extern_error_.used()) { danderson@3977: __ bind(&extern_error_); danderson@3977: __ movl(eax, Operand(info, AMX_INFO_CONTEXT)); danderson@3977: __ movl(eax, Operand(eax, offsetof(sp_context_t, n_err))); danderson@3977: __ jmp(g_Jit.GetUniversalReturn()); danderson@3977: } dvander@2226: } dvander@2226: danderson@3977: // Input dimension count is in ecx. danderson@3977: static void * danderson@3977: GenerateGenArrayIntrinsic(void *ret) dvander@157: { danderson@3977: AssemblerX86 masm; dvander@1908: danderson@3977: __ push(ebx); danderson@3977: __ push(eax); danderson@3977: __ push(edx); danderson@3977: __ push(ecx); // We reference this in the body of this routine. danderson@3977: danderson@3977: Label toobig; dvander@1908: danderson@3977: // Calculate how many cells will be needed. danderson@3977: __ movl(edx, Operand(stk, 0)); // Dimension's last count. danderson@3977: __ cmpl(edx, 0); danderson@3977: __ j(less, &toobig); danderson@3977: __ movl(eax, 1); // Position at second to last dimension. dvander@157: danderson@3977: Label loop, done; danderson@3977: __ bind(&loop); danderson@3977: __ cmpl(eax, Operand(esp, 0)); // Compare to # of dimensions. danderson@3977: __ j(not_below, &done); // End loop if done. danderson@3977: __ movl(ecx, Operand(stk, eax, ScaleFour)); // Get dimension size. danderson@3977: __ cmpl(ecx, 0); danderson@3977: __ j(less, &toobig); danderson@3977: __ imull(edx, ecx); // Multiply by size. danderson@3977: __ j(overflow, &toobig); danderson@3977: __ addl(eax, 1); // Increment dimension cursor. danderson@3977: __ addl(edx, ecx); // Add indirection vector size. danderson@3977: __ j(overflow, &toobig); danderson@3977: __ jmp(&loop); danderson@3977: __ bind(&done); dvander@2226: danderson@3977: // Test if we have space for the size in edx. danderson@3977: __ movl(eax, Operand(info, AMX_INFO_HEAP)); danderson@3977: __ imull(edx, edx, 4); danderson@3977: __ j(overflow, &toobig); danderson@3977: __ addl(eax, edx); danderson@3977: __ j(overflow, &toobig); danderson@3977: __ cmpl(eax, Operand(info, AMX_INFO_DATASIZE)); danderson@3977: __ j(not_above, &toobig); danderson@3977: __ addl(eax, dat); danderson@3977: __ cmpl(eax, stk); danderson@3977: __ j(not_below, &toobig); danderson@3977: __ shrl(edx, 2); // Undo the imull. dvander@2226: danderson@3977: // Allocate on the plugin heap. danderson@3977: __ movl(eax, Operand(info, AMX_INFO_HEAP)); // Get heap pointer. danderson@3977: __ lea(ebx, Operand(eax, edx, ScaleFour)); // New heap pointer. danderson@3977: __ movl(Operand(info, AMX_INFO_HEAP), ebx); // Store back. danderson@3977: __ push(eax); // Save on the stack. dvander@1908: danderson@3977: Label extern_error; danderson@3977: EmitTrackerPushReg(masm, edx, &extern_error); dvander@157: danderson@3977: // Call to C++ to generate indirection vectors. danderson@3977: __ lea(ebx, Operand(dat, eax, NoScale)); // relocate eax danderson@3977: __ push(Operand(esp, 8)); // push autozero danderson@3977: __ push(Operand(esp, 8)); // push dimension count danderson@3977: __ push(stk); // push dimension array danderson@3977: __ push(ebx); danderson@3977: __ call(ExternalAddress((void *)GenerateArrayIndirectionVectors)); danderson@3977: __ addl(esp, 4 * sizeof(void *)); dvander@157: danderson@3977: __ pop(eax); // Restore array pointer. danderson@3977: __ pop(ecx); // Restore dimension count. danderson@3977: __ lea(stk, Operand(stk, ecx, ScaleFour, -4)); // Pop params off the stack. danderson@3977: __ movl(Operand(stk, 0), eax); // Store back array pointer. danderson@3977: __ pop(edx); danderson@3977: __ pop(eax); danderson@3977: __ pop(ebx); danderson@3977: __ ret(); dvander@157: danderson@3977: __ bind(&toobig); danderson@3977: __ movl(eax, SP_ERROR_ARRAY_TOO_BIG); danderson@3977: __ jmp(ExternalAddress(ret)); danderson@3977: danderson@3977: __ bind(&extern_error); danderson@3977: __ jmp(ExternalAddress(ret)); danderson@3977: danderson@3977: return LinkCode(masm); dvander@1908: } dvander@1908: danderson@3977: static void * danderson@3977: GenerateEntry(void **retp) dvander@157: { danderson@3977: AssemblerX86 masm; danderson@3977: danderson@3977: // Variables we're passed in: danderson@3977: // void *vars[], void *entry danderson@3977: danderson@3977: __ push(ebp); danderson@3977: __ movl(ebp, esp); danderson@3977: danderson@3977: __ push(esi); danderson@3977: __ push(edi); danderson@3977: __ push(ebx); danderson@3977: danderson@3977: __ movl(esi, Operand(ebp, 8 + 4 * 0)); danderson@3977: __ movl(ecx, Operand(ebp, 8 + 4 * 1)); danderson@3977: __ movl(eax, Operand(esi, AMX_INFO_MEMORY)); danderson@3977: __ movl(edx, Operand(esi, AMX_INFO_CONTEXT)); danderson@3977: danderson@3977: // Set up run-time registers. danderson@3977: __ movl(edi, Operand(edx, offsetof(sp_context_t, sp))); danderson@3977: __ addl(edi, eax); danderson@3977: __ movl(ebp, eax); danderson@3977: __ movl(ebx, edi); danderson@3977: __ movl(Operand(esi, AMX_INFO_NSTACK), esp); dvander@157: danderson@3977: // Call into plugin. danderson@3977: __ call(ecx); faluco@853: danderson@3977: __ movl(ecx, Operand(info, AMX_INFO_RETVAL)); danderson@3977: __ movl(Operand(ecx, 0), pri); danderson@3977: __ movl(eax, SP_ERROR_NONE); dvander@157: danderson@3977: // If stuff goes wrong, it'll jump directly to here. danderson@3977: Label error; danderson@3977: __ bind(&error); danderson@3977: __ movl(esp, Operand(info, AMX_INFO_NSTACK)); danderson@3977: __ movl(ecx, Operand(info, AMX_INFO_CONTEXT)); danderson@3977: __ subl(stk, dat); danderson@3977: __ movl(Operand(ecx, offsetof(sp_context_t, sp)), stk); dvander@157: danderson@3977: __ pop(ebx); danderson@3977: __ pop(edi); danderson@3977: __ pop(esi); danderson@3977: __ pop(ebp); danderson@3977: __ ret(); dvander@157: danderson@3977: void *code = LinkCode(masm); danderson@3977: if (!code) danderson@3977: return NULL; dvander@157: danderson@3977: *retp = reinterpret_cast(code) + error.offset(); danderson@3977: return code; dvander@157: } dvander@157: dvander@2226: ICompilation *JITX86::ApplyOptions(ICompilation *_IN, ICompilation *_OUT) ds@1952: { danderson@3977: if (_IN == NULL) danderson@3977: return _OUT; danderson@3977: danderson@3977: CompData *_in = (CompData * )_IN; danderson@3977: CompData *_out = (CompData * )_OUT; dvander@2193: danderson@3977: _in->inline_level = _out->inline_level; danderson@3977: _in->profile = _out->profile; dvander@2226: danderson@3977: _out->Abort(); danderson@3977: return _in; dvander@2226: } dvander@2226: dvander@2226: JITX86::JITX86() dvander@2226: { danderson@3977: m_pJitEntry = NULL; danderson@3977: m_pJitGenArray = NULL; dvander@2226: } dvander@2226: dvander@2226: bool JITX86::InitializeJIT() dvander@157: { danderson@3977: g_pCodeCache = KE_CreateCodeCache(); dvander@2226: danderson@3977: m_pJitEntry = GenerateEntry(&m_pJitReturn); danderson@3977: if (!m_pJitEntry) danderson@3977: return false; dvander@2226: danderson@3977: m_pJitGenArray = GenerateGenArrayIntrinsic(m_pJitReturn); danderson@3977: if (!m_pJitGenArray) danderson@3977: return false; dvander@2226: danderson@3979: MacroAssemblerX86 masm; danderson@3979: MacroAssemblerX86::GenerateFeatureDetection(masm); danderson@3979: void *code = LinkCode(masm); danderson@3979: if (!code) danderson@3979: return false; danderson@3979: MacroAssemblerX86::RunFeatureDetection(code); danderson@3979: KE_FreeCode(g_pCodeCache, code); danderson@3979: danderson@3977: return true; dvander@2226: } dvander@2226: danderson@3977: void danderson@3977: JITX86::ShutdownJIT() dvander@2226: { danderson@3977: KE_DestroyCodeCache(g_pCodeCache); danderson@3977: } danderson@3977: danderson@3977: JitFunction * danderson@3977: JITX86::CompileFunction(BaseRuntime *prt, cell_t pcode_offs, int *err) danderson@3977: { danderson@3977: Compiler cc(prt, pcode_offs); danderson@3977: JitFunction *fun = cc.emit(err); danderson@3977: if (!fun) danderson@3977: return NULL; danderson@3977: prt->AddJittedFunction(fun); danderson@3977: return fun; dvander@2226: } dvander@2226: danderson@3977: void danderson@3977: JITX86::SetupContextVars(BaseRuntime *runtime, BaseContext *pCtx, sp_context_t *ctx) dvander@2226: { danderson@3977: tracker_t *trk = new tracker_t; faluco@831: danderson@3977: ctx->vm[JITVARS_TRACKER] = trk; danderson@3977: ctx->vm[JITVARS_BASECTX] = pCtx; /* GetDefaultContext() is not constructed yet */ danderson@3977: ctx->vm[JITVARS_PROFILER] = g_engine2.GetProfiler(); danderson@3977: ctx->vm[JITVARS_PLUGIN] = const_cast(runtime->plugin()); dvander@2226: danderson@3977: trk->pBase = (ucell_t *)malloc(1024); danderson@3977: trk->pCur = trk->pBase; danderson@3977: trk->size = 1024 / sizeof(cell_t); dvander@2226: } dvander@2226: danderson@3977: SPVM_NATIVE_FUNC danderson@3977: JITX86::CreateFakeNative(SPVM_FAKENATIVE_FUNC callback, void *pData) dvander@2226: { danderson@3977: AssemblerX86 masm; dvander@601: danderson@3977: __ push(ebx); danderson@3977: __ push(edi); danderson@3977: __ push(esi); danderson@3977: __ movl(edi, Operand(esp, 16)); // store ctx danderson@3977: __ movl(esi, Operand(esp, 20)); // store params danderson@3977: __ movl(ebx, esp); danderson@3977: __ andl(esp, 0xfffffff0); danderson@3977: __ subl(esp, 4); faluco@1166: danderson@3977: __ push(intptr_t(pData)); danderson@3977: __ push(esi); danderson@3977: __ push(edi); danderson@3977: __ call(ExternalAddress((void *)callback)); danderson@3977: __ movl(esp, ebx); danderson@3977: __ pop(esi); danderson@3977: __ pop(edi); danderson@3977: __ pop(ebx); danderson@3977: __ ret(); dvander@601: danderson@3977: return (SPVM_NATIVE_FUNC)LinkCode(masm); dvander@601: } dvander@601: dvander@605: void JITX86::DestroyFakeNative(SPVM_NATIVE_FUNC func) dvander@601: { danderson@3977: KE_FreeCode(g_pCodeCache, (void *)func); dvander@157: } dvander@157: dvander@2193: ICompilation *JITX86::StartCompilation() dvander@157: { danderson@3977: return new CompData; dvander@2193: } dvander@2193: dvander@2193: ICompilation *JITX86::StartCompilation(BaseRuntime *runtime) dvander@2193: { danderson@3977: return new CompData; dvander@157: } dvander@157: dvander@2193: void CompData::Abort() dvander@157: { danderson@3977: delete this; dvander@2193: } dvander@2193: dvander@2193: void JITX86::FreeContextVars(sp_context_t *ctx) dvander@2193: { danderson@3977: free(((tracker_t *)(ctx->vm[JITVARS_TRACKER]))->pBase); danderson@3977: delete (tracker_t *)ctx->vm[JITVARS_TRACKER]; ds@1952: } ds@1952: dvander@2193: bool CompData::SetOption(const char *key, const char *val) dvander@2193: { danderson@3977: if (strcmp(key, SP_JITCONF_DEBUG) == 0) danderson@3977: return true; danderson@3977: if (strcmp(key, SP_JITCONF_PROFILE) == 0) { danderson@3977: profile = atoi(val); dvander@157: danderson@3977: /** Callbacks must be profiled to profile functions! */ danderson@3977: if ((profile & SP_PROF_FUNCTIONS) == SP_PROF_FUNCTIONS) danderson@3977: profile |= SP_PROF_CALLBACKS; dvander@175: danderson@3977: return true; danderson@3977: } dvander@2226: danderson@3977: return false; dvander@344: } dvander@344: dvander@2226: typedef int (*JIT_EXECUTE)(cell_t *vars, void *addr); dvander@2226: int JITX86::InvokeFunction(BaseRuntime *runtime, JitFunction *fn, cell_t *result) dvander@344: { danderson@3977: int err; danderson@3977: JIT_EXECUTE pfn; danderson@3977: sp_context_t *ctx; danderson@3977: cell_t vars[AMX_NUM_INFO_VARS]; dvander@2226: danderson@3977: ctx = runtime->GetBaseContext()->GetCtx(); dvander@2226: danderson@3977: vars[0] = ctx->sp; danderson@3977: vars[1] = ctx->hp; danderson@3977: vars[2] = (cell_t)result; danderson@3977: vars[3] = (cell_t)ctx; danderson@3977: vars[4] = (cell_t)(runtime->plugin()->memory + runtime->plugin()->mem_size); danderson@3977: vars[5] = fn->GetPCodeAddress(); danderson@3977: vars[6] = runtime->plugin()->data_size; danderson@3977: vars[7] = (cell_t)(runtime->plugin()->memory); danderson@3977: /* vars[8] will be set to ESP */ dvander@2226: danderson@3977: pfn = (JIT_EXECUTE)m_pJitEntry; danderson@3977: err = pfn(vars, fn->GetEntryAddress()); dvander@2226: danderson@3977: ctx->hp = vars[1]; danderson@3977: ctx->err_cip = vars[5]; dvander@2226: danderson@3977: return err; dvander@175: } dvander@175: dvander@2226: void *JITX86::AllocCode(size_t size) dvander@175: { danderson@3977: return Knight::KE_AllocCode(g_pCodeCache, size); dvander@175: } dvander@253: dvander@2226: void JITX86::FreeCode(void *code) dvander@253: { danderson@3977: KE_FreeCode(g_pCodeCache, code); dvander@253: }