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