Skip to content

Instantly share code, notes, and snippets.

@Zeex
Created February 13, 2012 16:33
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Zeex/1818028 to your computer and use it in GitHub Desktop.
Save Zeex/1818028 to your computer and use it in GitHub Desktop.
#include <assert.h>
#include <stddef.h> /* for size_t */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#if defined LINUX
#include <sys/mman.h>
#else
#include <Windows.h>
#endif
#if __STDC_VERSION__ >= 199901L
#define HAVE_STDINT_H 1
#include <stdint.h>
#include <stdbool.h>
#else
typedef unsigned char bool;
enum { false, true };
#endif
#if defined __GNUC__
#if defined __CYGWIN__ || defined __MINGW32__
#define PLUGIN_CALL __attribute__((stdcall))
#else
#define PLUGIN_CALL __attribute__((cdecl))
#endif
#define PLUGIN_EXPORT __attribute__((visibility("default")))
#elif defined _MSC_VER
#define PLUGIN_CALL __stdcall
#define PLUGIN_EXPORT __declspec(dllexport)
#endif
#if !defined(__BYTE_ORDER)
/* we are targeting x86 which is Little Endian */
#define __LITTLE_ENDIAN 1234
#define __BYTE_ORDER __LITTLE_ENDIAN
#endif
#include <amx/amx.h>
#include <lightning.h>
#define NOT_IMPLEMENTED(op) \
do { \
logprintf("JIT: the " op " instruction is not implemented"); \
abort(); \
} while (false)
typedef void (*logprintf_t)(const char *format, ...);
/* maximum size of generated JIT code */
#define MAX_JIT_SIZE 10240
/* opcode list from amx.c */
typedef enum {
OP_NONE, /* invalid opcode */
OP_LOAD_PRI,
OP_LOAD_ALT,
OP_LOAD_S_PRI,
OP_LOAD_S_ALT,
OP_LREF_PRI,
OP_LREF_ALT,
OP_LREF_S_PRI,
OP_LREF_S_ALT,
OP_LOAD_I,
OP_LODB_I,
OP_CONST_PRI,
OP_CONST_ALT,
OP_ADDR_PRI,
OP_ADDR_ALT,
OP_STOR_PRI,
OP_STOR_ALT,
OP_STOR_S_PRI,
OP_STOR_S_ALT,
OP_SREF_PRI,
OP_SREF_ALT,
OP_SREF_S_PRI,
OP_SREF_S_ALT,
OP_STOR_I,
OP_STRB_I,
OP_LIDX,
OP_LIDX_B,
OP_IDXADDR,
OP_IDXADDR_B,
OP_ALIGN_PRI,
OP_ALIGN_ALT,
OP_LCTRL,
OP_SCTRL,
OP_MOVE_PRI,
OP_MOVE_ALT,
OP_XCHG,
OP_PUSH_PRI,
OP_PUSH_ALT,
OP_PUSH_R,
OP_PUSH_C,
OP_PUSH,
OP_PUSH_S,
OP_POP_PRI,
OP_POP_ALT,
OP_STACK,
OP_HEAP,
OP_PROC,
OP_RET,
OP_RETN,
OP_CALL,
OP_CALL_PRI,
OP_JUMP,
OP_JREL,
OP_JZER,
OP_JNZ,
OP_JEQ,
OP_JNEQ,
OP_JLESS,
OP_JLEQ,
OP_JGRTR,
OP_JGEQ,
OP_JSLESS,
OP_JSLEQ,
OP_JSGRTR,
OP_JSGEQ,
OP_SHL,
OP_SHR,
OP_SSHR,
OP_SHL_C_PRI,
OP_SHL_C_ALT,
OP_SHR_C_PRI,
OP_SHR_C_ALT,
OP_SMUL,
OP_SDIV,
OP_SDIV_ALT,
OP_UMUL,
OP_UDIV,
OP_UDIV_ALT,
OP_ADD,
OP_SUB,
OP_SUB_ALT,
OP_AND,
OP_OR,
OP_XOR,
OP_NOT,
OP_NEG,
OP_INVERT,
OP_ADD_C,
OP_SMUL_C,
OP_ZERO_PRI,
OP_ZERO_ALT,
OP_ZERO,
OP_ZERO_S,
OP_SIGN_PRI,
OP_SIGN_ALT,
OP_EQ,
OP_NEQ,
OP_LESS,
OP_LEQ,
OP_GRTR,
OP_GEQ,
OP_SLESS,
OP_SLEQ,
OP_SGRTR,
OP_SGEQ,
OP_EQ_C_PRI,
OP_EQ_C_ALT,
OP_INC_PRI,
OP_INC_ALT,
OP_INC,
OP_INC_S,
OP_INC_I,
OP_DEC_PRI,
OP_DEC_ALT,
OP_DEC,
OP_DEC_S,
OP_DEC_I,
OP_MOVS,
OP_CMPS,
OP_FILL,
OP_HALT,
OP_BOUNDS,
OP_SYSREQ_PRI,
OP_SYSREQ_C,
OP_FILE, /* obsolete */
OP_LINE, /* obsolete */
OP_SYMBOL, /* obsolete */
OP_SRANGE, /* obsolete */
OP_JUMP_PRI,
OP_SWITCH,
OP_CASETBL,
OP_SWAP_PRI,
OP_SWAP_ALT,
OP_PUSHADDR,
OP_NOP,
OP_SYSREQ_D,
OP_SYMTAG, /* obsolete */
OP_BREAK,
/* ----- */
NUM_OPCODES
} OPCODE;
static logprintf_t logprintf;
struct list {
int key;
void *value;
struct list *next;
};
struct fn_info {
cell amx_address;
void *jit_address;
bool is_public;
struct list *refs;
};
struct fn_ref {
void *where;
};
struct amx_info {
jit_insn *jit_code;
size_t jit_code_size;
};
static struct list *amx_list;
static void list_insert(struct list **root, int key, void *value) {
struct list *node;
node = malloc(sizeof(struct list));
node->key = key;
node->value = value;
node->next = *root;
*root = node;
}
static void *list_get(struct list *root, int key) {
struct list *cur;
cur = root;
while (cur != NULL) {
if (cur->key == key) {
return cur->value;
}
cur = cur->next;
}
return NULL;
}
static void *list_remove(struct list *root, int key) {
struct list *cur;
struct list *prev;
void *value;
cur = root;
prev = NULL;
while (cur != NULL) {
if (cur->key == key) {
value = cur->value;
if (prev != NULL) {
prev->next = cur->next;
}
free(cur);
return value;
}
prev = cur;
cur = cur->next;
}
return NULL;
}
static void list_free(struct list **root) {
struct list *cur;
struct list *prev;
cur = *root;
prev = NULL;
while (cur != NULL) {
prev = cur;
cur = cur->next;
free(prev);
}
*root = NULL;
}
static int find_public(AMX *amx, cell address) {
AMX_HEADER *hdr;
AMX_FUNCSTUBNT *publics;
int index, num_publics;
hdr = (AMX_HEADER*)amx->base;
publics = (AMX_FUNCSTUBNT*)(amx->base + hdr->publics);
num_publics = (hdr->natives - hdr->publics) / hdr->defsize;
for (index = 0; index < num_publics; index++) {
if (publics[index].address == address) {
return index;
}
}
return -1;
}
static bool is_public(AMX *amx, cell address) {
AMX_HEADER *hdr;
hdr = (AMX_HEADER*)amx->base;
if (address == hdr->cip) {
/* main() */
return true;
}
return find_public(amx, address) >= 0;
}
static int set_public_address(AMX *amx, int index, cell address) {
AMX_HEADER *hdr;
AMX_FUNCSTUBNT *publics;
hdr = (AMX_HEADER*)amx->base;
publics = (AMX_FUNCSTUBNT*)(amx->base + hdr->publics);
if (index >= 0) {
publics[index].address = address;
}
return index;
}
static void *get_public_address(AMX *amx, cell index) {
AMX_HEADER *hdr;
AMX_FUNCSTUBNT *publics;
hdr = (AMX_HEADER*)amx->base;
publics = (AMX_FUNCSTUBNT*)(amx->base + hdr->publics);
return (void*)publics[index].address;
}
static void *get_native_address(AMX *amx, cell index) {
AMX_HEADER *hdr;
AMX_FUNCSTUBNT *natives;
hdr = (AMX_HEADER*)amx->base;
natives = (AMX_FUNCSTUBNT*)(amx->base + hdr->natives);
return (void*)natives[index].address;
}
static void dump_jit_code(const char *filename, jit_insn *code, size_t size) {
FILE *stream;
if ((stream = fopen(filename, "w")) != NULL) {
fwrite(code, sizeof(jit_insn), size, stream);
fclose(stream);
}
}
static void jit_amx(AMX *amx, jit_insn *jitcode, size_t size)
{
AMX_HEADER *hdr;
unsigned char *code, *data;
cell code_size, data_size;
cell *cip, opcode, arg;
struct list *fn_list = NULL;
struct fn_info *fn;
jit_insn **amx2jit;
jit_set_ip(jitcode);
hdr = (AMX_HEADER*)amx->base;
code = amx->base + hdr->cod;
code_size = hdr->dat - hdr->cod;
/* maps AMX code to JIT */
amx2jit = malloc(sizeof(*amx2jit) * code_size);
data = (amx->data != 0) ? amx->data : amx->base + hdr->dat;
data_size = hdr->hea - hdr->dat;
cip = (cell*)(code + hdr->cip);
while ((int)cip < (int)(code + code_size)) {
amx2jit[(int)cip - (int)code] = jit_get_label();
opcode = *cip++;
arg = *cip;
switch (opcode) {
case OP_LOAD_PRI: /* address */
/* PRI = [address] */
jit_movi_i(JIT_V0, (int)(data + arg));
jit_ldr_i(JIT_R0, JIT_V0);
cip++;
break;
case OP_LOAD_ALT: /* address */
/* PRI = [address] */
jit_movi_i(JIT_V0, (int)(data + arg));
jit_ldr_i(JIT_R1, JIT_V0);
cip++;
break;
case OP_LOAD_S_PRI: /* offset */
/* PRI = [FRM + offset] */
jit_addi_i(JIT_V0, JIT_FP, arg);
jit_ldr_i(JIT_R0, JIT_V0);
cip++;
break;
case OP_LOAD_S_ALT: /* offset */
/* ALT = [FRM + offset] */
jit_addi_i(JIT_V0, JIT_FP, arg);
jit_ldr_i(JIT_R1, JIT_V0);
cip++;
break;
case OP_LREF_PRI: /* address */
/* PRI = [ [address] ] */
jit_movi_i(JIT_V0, (int)(data + arg));
jit_ldr_i(JIT_V0, JIT_V0);
jit_ldr_i(JIT_R0, JIT_V0);
cip++;
break;
case OP_LREF_ALT: /* address */
/* ALT = [ [address] ] */
jit_movi_i(JIT_V0, (int)(data + arg));
jit_ldr_i(JIT_V0, JIT_V0);
jit_ldr_i(JIT_R1, JIT_V0);
cip++;
break;
case OP_LREF_S_PRI: /* offset */
/* PRI = [ [FRM + offset] ] */
jit_addi_i(JIT_V0, JIT_FP, arg);
jit_ldr_i(JIT_V0, JIT_V0);
jit_ldr_i(JIT_R0, JIT_V0);
cip++;
break;
case OP_LREF_S_ALT: /* offset */
/* PRI = [ [FRM + offset] ] */
jit_addi_i(JIT_V0, JIT_FP, arg);
jit_ldr_i(JIT_V0, JIT_V0);
jit_ldr_i(JIT_R1, JIT_V0);
cip++;
break;
case OP_LOAD_I:
/* PRI = [PRI] (full cell) */
jit_ldr_i(JIT_R0, JIT_R0);
break;
case OP_LODB_I: /* number */
/* PRI = “number” bytes from [PRI] (read 1/2/4 bytes) */
switch (arg) {
case 1:
jit_ldr_c(JIT_R0, JIT_R0);
break;
case 2:
jit_ldr_s(JIT_R0, JIT_R0);
break;
default:
jit_ldr_i(JIT_R0, JIT_R0);
}
cip++;
break;
case OP_CONST_PRI: /* value */
/* PRI = value */
jit_movr_i(JIT_R0, arg);
cip++;
break;
case OP_CONST_ALT: /* value */
/* ALT = value */
jit_movr_i(JIT_R1, arg);
cip++;
break;
case OP_ADDR_PRI: /* offset */
/* PRI = FRM + offset */
jit_addi_i(JIT_R0, JIT_FP, arg);
cip++;
break;
case OP_ADDR_ALT: /* offset */
/* ALT = FRM + offset */
jit_addi_i(JIT_R1, JIT_FP, arg);
cip++;
break;
case OP_STOR_PRI: /* address */
/* [address] = PRI */
jit_sti_i((int)(data + arg), JIT_R0);
cip++;
break;
case OP_STOR_ALT: /* address */
/* [address] = ALT */
jit_sti_i((int)(data + arg), JIT_R1);
cip++;
break;
case OP_STOR_S_PRI: /* offset */
/* [FRM + offset] = ALT */
jit_addi_i(JIT_V0, JIT_FP, arg);
jit_str_i(JIT_V0, JIT_R0);
cip++;
break;
case OP_STOR_S_ALT: /* offset */
/* [FRM + offset] = ALT */
jit_addi_i(JIT_V0, JIT_FP, arg);
jit_str_i(JIT_V0, JIT_R1);
cip++;
break;
case OP_SREF_PRI: /* address */
/* [ [address] ] = PRI */
jit_movi_i(JIT_V0, (int)(data + arg));
jit_ldr_i(JIT_V0, JIT_V0);
jit_str_i(JIT_V0, JIT_R0);
cip++;
break;
case OP_SREF_ALT: /* address */
/* [ [address] ] = ALT */
jit_movi_i(JIT_V0, (int)(data + arg));
jit_ldr_i(JIT_V0, JIT_V0);
jit_str_i(JIT_V0, JIT_R1);
cip++;
break;
case OP_SREF_S_PRI: /* offset */
/* [ [FRM + offset] ] = PRI */
jit_addi_i(JIT_V0, JIT_FP, arg);
jit_ldr_i(JIT_V0, JIT_V0);
jit_str_i(JIT_V0, JIT_R0);
cip++;
break;
case OP_SREF_S_ALT: /* offset */
/* [ [FRM + offset] ] = ALT */
jit_addi_i(JIT_V0, JIT_FP, arg);
jit_ldr_i(JIT_V0, JIT_V0);
jit_str_i(JIT_V0, JIT_R1);
cip++;
break;
case OP_STOR_I:
/* [ALT] = PRI (full cell) */
jit_ldr_i(JIT_R1, JIT_R0);
break;
case OP_STRB_I: /* number */
/* “number” bytes at [ALT] = PRI (write 1/2/4 bytes) */
switch (arg) {
case 1:
jit_str_c(JIT_R1, JIT_R0);
break;
case 2:
jit_str_s(JIT_R1, JIT_R0);
break;
default:
jit_str_i(JIT_R1, JIT_R0);
}
cip++;
break;
case OP_LIDX:
/* PRI = [ ALT + (PRI x cell size) ] */
jit_muli_ui(JIT_V0, JIT_R0, sizeof(cell));
jit_addr_i(JIT_V0, JIT_R1, JIT_V0);
jit_ldr_i(JIT_R0, JIT_V0);
break;
case OP_LIDX_B: /* shift */
/* PRI = [ ALT + (PRI << shift) ] */
jit_muli_ui(JIT_V0, JIT_R0, arg);
jit_addr_i(JIT_V0, JIT_R1, JIT_V0);
jit_ldr_i(JIT_R0, JIT_V0);
cip++;
break;
case OP_IDXADDR:
/* PRI = ALT + (PRI x cell size) (calculate indexed address) */
jit_muli_ui(JIT_V0, JIT_R0, sizeof(cell));
jit_addr_i(JIT_R0, JIT_R1, JIT_V0);
break;
case OP_IDXADDR_B: /* shift */
/* PRI = ALT + (PRI << shift) (calculate indexed address) */
jit_muli_ui(JIT_V0, JIT_R0, arg);
jit_addr_i(JIT_R0, JIT_R1, JIT_V0);
cip++;
break;
case OP_ALIGN_PRI: /* number */
/* Little Endian: PRI ^= cell size - number */
#if __BYTE_ORDER == __LITTLE_ENDIAN
jit_xori_i(JIT_R0, JIT_R0, sizeof(cell) - arg);
#endif
cip++;
break;
case OP_ALIGN_ALT: /* number */
/* Little Endian: ALT ^= cell size - number */
#if __BYTE_ORDER == __LITTLE_ENDIAN
jit_xori_i(JIT_R1, JIT_R0, sizeof(cell) - arg);
#endif
cip++;
break;
case OP_LCTRL: /* index */
/* PRI is set to the current value of any of the special registers.
* The index parameter must be: 0=COD, 1=DAT, 2=HEA,
* 3=STP, 4=STK, 5=FRM, 6=CIP (of the next instruction)
*/
NOT_IMPLEMENTED("LCTRL");
break;
case OP_SCTRL: /* index */
/* set the indexed special registers to the value in PRI.
* The index parameter must be: 2=HEA, 4=STK, 5=FRM,
* 6=CIP
*/
NOT_IMPLEMENTED("SCTRL");
break;
case OP_MOVE_PRI:
/* PRI = ALT */
jit_movr_i(JIT_R0, JIT_R1);
break;
case OP_MOVE_ALT:
/* ALT = PRI */
jit_movr_i(JIT_R1, JIT_R0);
break;
case OP_XCHG:
/* Exchange PRI and ALT */
jit_movr_i(JIT_V0, JIT_R0);
jit_movr_i(JIT_R0, JIT_R1);
jit_movr_i(JIT_R1, JIT_V0);
break;
case OP_PUSH_PRI:
/* [STK] = PRI/ALT, STK = STK - cell size */
jit_ldr_i(JIT_SP, JIT_R0);
jit_subi_i(JIT_SP, JIT_SP, (int)sizeof(int));
break;
case OP_PUSH_ALT:
jit_ldr_i(JIT_SP, JIT_R1);
jit_subi_i(JIT_SP, JIT_SP, (int)sizeof(int));
break;
case OP_PUSH_R: /* value */
/* obsolete */
break;
case OP_PUSH_C: /* value */
/* [STK] = value, STK = STK - cell size */
jit_movi_i(JIT_V0, arg);
jit_str_i(JIT_SP, JIT_V0);
jit_subi_i(JIT_SP, JIT_SP, (int)sizeof(int));
cip++;
break;
case OP_PUSH: /* address */
/* [STK] = [address], STK = STK - cell size */
jit_ldi_i(JIT_V0, (int)(data + arg));
jit_ldr_i(JIT_SP, JIT_V0);
jit_subi_i(JIT_SP, JIT_SP, (int)sizeof(int));
cip++;
break;
case OP_PUSH_S: /* offset */
/* [STK] = [FRM + offset], STK = STK - cell size */
jit_ldxi_i(JIT_SP, JIT_FP, arg);
jit_subi_i(JIT_SP, JIT_SP, (int)sizeof(int));
cip++;
break;
case OP_POP_PRI:
/* STK = STK + cell size, PRI = [STK] */
jit_ldr_i(JIT_R0, JIT_SP);
jit_addi_i(JIT_SP, JIT_SP, (int)sizeof(int));
break;
case OP_POP_ALT:
/* STK = STK + cell size, ALT = [STK] */
jit_ldr_i(JIT_R1, JIT_SP);
jit_addi_i(JIT_SP, JIT_SP, (int)sizeof(int));
break;
case OP_STACK: /* value */
/* ALT = STK, STK = STK + value */
jit_movr_i(JIT_R1, JIT_SP);
jit_addi_i(JIT_SP, JIT_SP, arg);
cip++;
break;
case OP_HEAP:
NOT_IMPLEMENTED("HEAP");
break;
case OP_PROC:
/* [STK] = FRM, STK = STK - cell size, FRM = STK */
fn = list_get(fn_list, (int)(cip - 1) - (int)code);
if (fn == NULL) {
fn = malloc(sizeof(*fn));
fn->amx_address = (int)(cip - 1) - (int)code;
fn->jit_address = jit_get_ip().vptr;
fn->is_public = is_public(amx, fn->amx_address);
fn->refs = NULL; /* no references */
if (fn->amx_address == hdr->cip) {
hdr->cod = (cell)fn->jit_address;
} else {
set_public_address(amx, find_public(amx, fn->amx_address), (cell)fn->jit_address);
}
list_insert(&fn_list, fn->amx_address, fn);
} else {
fn->jit_address = jit_get_ip().vptr;
}
if (fn->is_public) {
jit_prolog(0);
} else {
jit_str_i(JIT_SP, JIT_FP);
jit_subi_i(JIT_SP, JIT_SP, (int)sizeof(cell));
jit_movr_i(JIT_FP, JIT_SP);
}
break;
case OP_RET:
/* STK = STK + cell size, FRM = [STK],
* STK = STK + cell size, CIP = [STK],
* The RET instruction cleans up the stack frame and returns
* from the function to the instruction after the call.
*/
if (fn->is_public) {
jit_ret();
} else {
jit_addi_i(JIT_SP, JIT_SP, sizeof(cell));
jit_ldr_i(JIT_FP, JIT_SP);
jit_addi_i(JIT_SP, JIT_SP, sizeof(cell));
jit_ldr_i(JIT_V0, JIT_SP);
jit_jmpr(JIT_V0);
}
break;
case OP_RETN:
/* FRM = [STK], STK = STK + cell size,
* CIP = [STK], STK = STK + cell size,
* STK = STK + [STK]
* The RETN instruction removes a specified number of bytes
* from the stack. The value to adjust STK with must be
* pushed prior to the call.
*/
if (fn->is_public) {
jit_ret();
} else {
jit_ldr_i(JIT_FP, JIT_SP); /* [SP] = FP */
jit_addi_i(JIT_SP, JIT_SP, sizeof(cell)); /* SP += sizeof(cell) */
jit_ldr_i(JIT_V0, JIT_SP); /* V0 = [SP] ; return address */
jit_addi_i(JIT_SP, JIT_SP, sizeof(cell)); /* SP += sizeof(cell) */
jit_ldr_i(JIT_V1, JIT_SP); /* V1 = [SP] ; number of bytes */
jit_addr_i(JIT_SP, JIT_SP, JIT_V1); /* SP += V1 ; pop arguments */
jit_jmpr(JIT_V0); /* JUMP [V0] */
}
break;
case OP_CALL: {
/* [STK] = CIP + 5, STK = STK - cell size
* CIP = CIP + offset
* The CALL instruction jumps to an address after storing the
* address of the next sequential instruction on the stack.
* The address jumped to is relative to the current CIP,
* but the address on the stack is an absolute address.
*/
struct fn_info *callee;
callee = list_get(fn_list, arg - (int)code);
if (callee == NULL) {
callee = malloc(sizeof(*callee));
callee->amx_address = arg - (int)code;
callee->jit_address = NULL; /* don't know the address yet */
callee->is_public = is_public(amx, callee->amx_address);
callee->refs = NULL; /* no references */
list_insert(&fn_list, callee->amx_address, callee);
}
if (callee->is_public) {
list_insert(&(callee->refs), (int)jit_get_ip().ptr, NULL);
jit_calli(0);
} else {
jit_insn *ref;
jit_movi_i(JIT_V0, (int)jit_forward());
ref = jit_forward();
jit_str_i(JIT_SP, JIT_V0);
list_insert(&(callee->refs), (int)jit_get_ip().ptr, NULL);
jit_jmpr(0);
/* HACK: subtract "ref" from IP to make jit_patch() insert
* an absolute address.
*/
jit_set_ip((jit_insn*)((int)jit_get_ip().ptr + (int)ref));
jit_patch((int)ref);
jit_set_ip((jit_insn*)((int)jit_get_ip().ptr - (int)ref));
}
cip++;
break;
}
case OP_CALL_PRI:
/* obsolete */
break;
case OP_JUMP: /* offset */
/* CIP = CIP + offset (jump to the address relative from
* the current position)
*/
NOT_IMPLEMENTED("JUMP");
break;
case OP_JREL: /* offset */
/* obsolete */
break;
case OP_JZER: /* offset */
/* if PRI == 0 then CIP = CIP + offset */
NOT_IMPLEMENTED("JZER");
break;
case OP_JNZ: /* offset */
/* if PRI != 0 then CIP = CIP + offset */
NOT_IMPLEMENTED("JNZ");
break;
case OP_JEQ: /* offset */
/* if PRI == ALT then CIP = CIP + offset */
NOT_IMPLEMENTED("JEQ");
break;
case OP_JNEQ: /* offset */
/* if PRI != ALT then CIP = CIP + offset */
NOT_IMPLEMENTED("JNEQ");
break;
case OP_JLESS: /* offset */
/* if PRI < ALT then CIP = CIP + offset (unsigned) */
NOT_IMPLEMENTED("JLESS");
break;
case OP_JLEQ: /* offset */
/* if PRI <= ALT then CIP = CIP + offset (unsigned) */
NOT_IMPLEMENTED("JLEQ");
break;
case OP_JGRTR: /* offset */
/* if PRI > ALT then CIP = CIP + offset (unsigned) */
NOT_IMPLEMENTED("JGRTR");
break;
case OP_JGEQ: /* offset */
/* if PRI >= ALT then CIP = CIP + offset (unsigned) */
NOT_IMPLEMENTED("JGEQ");
break;
case OP_JSLESS: /* offset */
/* if PRI < ALT then CIP = CIP + offset (signed) */
NOT_IMPLEMENTED("JLESS");
break;
case OP_JSLEQ: /* offset */
/* if PRI <= ALT then CIP = CIP + offset (signed) */
NOT_IMPLEMENTED("JSLEQ");
break;
case OP_JSGRTR: /* offset */
/* if PRI > ALT then CIP = CIP + offset (signed) */
NOT_IMPLEMENTED("JSGRTR");
break;
case OP_JSGEQ: /* offset */
/* if PRI >= ALT then CIP = CIP + offset (signed) */
NOT_IMPLEMENTED("JSGEQ");
break;
case OP_SHL:
/* PRI = PRI << ALT */
jit_lshr_ui(JIT_R0, JIT_R0, JIT_R1);
break;
case OP_SHR:
/* PRI = PRI >> ALT (without sign extension) */
jit_rshr_ui(JIT_R0, JIT_R0, JIT_R1);
break;
case OP_SSHR:
/* PRI = PRI >> ALT with sign extension */
jit_rshr_i(JIT_R0, JIT_R0, JIT_R1);
break;
case OP_SHL_C_PRI: /* value */
/* PRI = PRI << value */
jit_lshi_ui(JIT_R0, JIT_R0, arg);
cip++;
break;
case OP_SHL_C_ALT: /* value */
/* ALT = ALT << value */
jit_lshi_ui(JIT_R1, JIT_R1, arg);
cip++;
break;
case OP_SHR_C_PRI: /* value */
/* PRI = PRI >> value (without sign extension) */
jit_rshr_ui(JIT_R0, JIT_R0, arg);
cip++;
break;
case OP_SHR_C_ALT: /* value */
/* PRI = PRI >> value (without sign extension) */
jit_rshr_ui(JIT_R1, JIT_R1, arg);
cip++;
break;
case OP_SMUL:
/* PRI = PRI * ALT (signed multiply) */
jit_mulr_i(JIT_R0, JIT_R0, JIT_R1);
break;
case OP_SDIV:
/* PRI = PRI / ALT (signed divide), ALT = PRI mod ALT */
jit_divr_i(JIT_R0, JIT_R0, JIT_R1);
jit_modr_i(JIT_R1, JIT_R0, JIT_R1);
break;
case OP_SDIV_ALT:
/* PRI = ALT / PRI (signed divide), ALT = ALT mod PRI */
jit_divr_i(JIT_R0, JIT_R1, JIT_R0);
jit_modr_i(JIT_R1, JIT_R1, JIT_R0);
break;
case OP_UMUL:
/* PRI = PRI * ALT (unsigned multiply) */
jit_mulr_ui(JIT_R0, JIT_R0, JIT_R1);
break;
case OP_UDIV:
/* PRI = PRI / ALT (unsigned divide), ALT = PRI mod ALT */
jit_divr_ui(JIT_R0, JIT_R0, JIT_R1);
jit_modr_ui(JIT_R1, JIT_R0, JIT_R1);
break;
case OP_UDIV_ALT:
/* PRI = ALT / PRI (unsigned divide), ALT = ALT mod PRI */
jit_divr_ui(JIT_R0, JIT_R1, JIT_R0);
jit_modr_ui(JIT_R1, JIT_R1, JIT_R0);
break;
case OP_ADD:
/* PRI = PRI + ALT */
jit_addr_i(JIT_R0, JIT_R0, JIT_R1);
break;
case OP_SUB:
/* PRI = PRI - ALT */
jit_subr_i(JIT_R0, JIT_R0, JIT_R1);
break;
case OP_SUB_ALT:
/* PRI = ALT - PRI */
jit_subr_i(JIT_R0, JIT_R1, JIT_R0);
break;
case OP_AND:
/* PRI = PRI & ALT */
jit_andr_i(JIT_R0, JIT_R0, JIT_R1);
break;
case OP_OR:
/* PRI = PRI | ALT */
jit_orr_i(JIT_R0, JIT_R0, JIT_R1);
break;
case OP_XOR:
/* PRI = PRI ^ ALT */
jit_xorr_i(JIT_R0, JIT_R0, JIT_R1);
break;
case OP_NOT:
/* PRI = !PRI */
jit_eqi_i(JIT_R0, JIT_R0, 0);
break;
case OP_NEG:
/* PRI = -PRI */
jit_negr_i(JIT_R0, JIT_R0);
break;
case OP_INVERT:
/* PRI = ~PRI */
jit_notr_i(JIT_R0, JIT_R0);
break;
case OP_ADD_C: /* value */
/* PRI = PRI + value */
jit_addr_i(JIT_R0, JIT_R0, arg);
cip++;
break;
case OP_SMUL_C: /* value */
/* PRI = PRI * value */
jit_mulr_i(JIT_R0, JIT_R0, arg);
cip++;
break;
case OP_ZERO_PRI:
/* PRI = 0 */
jit_movi_i(JIT_R0, 0);
break;
case OP_ZERO_ALT:
/* ALT = 0 */
jit_movi_i(JIT_R1, 0);
break;
case OP_ZERO: /* address */
/* [address] = 0 */
jit_sti_i((int)(data + arg), 0);
cip++;
break;
case OP_ZERO_S: /* offset */
/* [FRM + offset] = 0 */
jit_addi_i(JIT_V0, JIT_FP, arg);
jit_sti_i(JIT_V0, 0);
cip++;
break;
case OP_SIGN_PRI:
/* sign extent the byte in PRI to a cell */
jit_extr_c_i(JIT_R0, JIT_R0);
break;
case OP_SIGN_ALT:
/* sign extent the byte in ALT to a cell */
jit_extr_c_i(JIT_R1, JIT_R1);
break;
case OP_EQ:
/* PRI = PRI == ALT ? 1 : 0 */
jit_eqr_i(JIT_R0, JIT_R0, JIT_R1);
break;
case OP_NEQ:
/* PRI = PRI != ALT ? 1 : 0 */
jit_ner_i(JIT_R0, JIT_R0, JIT_R1);
break;
case OP_LESS:
/* PRI = PRI < ALT ? 1 : 0 (unsigned) */
jit_ltr_ui(JIT_R0, JIT_R0, JIT_R1);
break;
case OP_LEQ:
/* PRI = PRI <= ALT ? 1 : 0 (unsigned) */
jit_ler_ui(JIT_R0, JIT_R0, JIT_R1);
break;
case OP_GRTR:
/* PRI = PRI > ALT ? 1 : 0 (unsigned) */
jit_gtr_ui(JIT_R0, JIT_R0, JIT_R1);
break;
case OP_GEQ:
/* PRI = PRI >= ALT ? 1 : 0 (unsigned) */
jit_ger_ui(JIT_R0, JIT_R0, JIT_R1);
break;
case OP_SLESS:
/* PRI = PRI < ALT ? 1 : 0 (signed) */
jit_ltr_i(JIT_R0, JIT_R0, JIT_R1);
break;
case OP_SLEQ:
/* PRI = PRI <= ALT ? 1 : 0 (signed) */
jit_ler_i(JIT_R0, JIT_R0, JIT_R1);
break;
case OP_SGRTR:
/* PRI = PRI > ALT ? 1 : 0 (signed) */
jit_gtr_i(JIT_R0, JIT_R0, JIT_R1);
break;
case OP_SGEQ:
/* PRI = PRI >= ALT ? 1 : 0 (signed) */
jit_ger_i(JIT_R0, JIT_R0, JIT_R1);
break;
case OP_EQ_C_PRI: /* value */
/* PRI = PRI == value ? 1 : 0 */
jit_eqi_i(JIT_R0, JIT_R0, arg);
cip++;
break;
case OP_EQ_C_ALT: /* value */
/* PRI = ALT == value ? 1 : 0 */
jit_eqi_i(JIT_R0, JIT_R1, arg);
cip++;
break;
case OP_INC_PRI:
/* PRI = PRI + 1 */
jit_addi_i(JIT_R0, JIT_R0, 1);
break;
case OP_INC_ALT:
/* ALT = ALT + 1 */
jit_addi_i(JIT_R1, JIT_R1, 1);
break;
case OP_INC: /* address */
/* [address] = [address] + 1 */
jit_ldi_i(JIT_V0, (int)(data + arg));
jit_addi_i(JIT_V0, JIT_V0, 1);
jit_str_i((int)(data + arg), JIT_V0);
cip++;
break;
case OP_INC_S: /* offset */
/* [FRM + offset] = [FRM + offset] + 1 */
jit_addi_i(JIT_V0, JIT_FP, arg);
jit_ldr_i(JIT_V1, JIT_V0);
jit_addi_i(JIT_V1, JIT_V1, 1);
jit_str_i(JIT_V0, JIT_V1);
cip++;
break;
case OP_INC_I:
/* [PRI] = [PRI] + 1 */
jit_ldr_i(JIT_V0, JIT_R0);
jit_addi_i(JIT_V0, JIT_V0, 1);
jit_str_i(JIT_R0, JIT_V0);
break;
case OP_DEC_PRI:
/* PRI = PRI - 1 */
jit_subi_i(JIT_R0, JIT_R0, 1);
break;
case OP_DEC_ALT:
/* ALT = ALT - 1 */
jit_subi_i(JIT_R1, JIT_R1, 1);
break;
case OP_DEC: /* address */
/* [address] = [address] - 1 */
jit_ldi_i(JIT_V0, (int)(data + arg));
jit_subi_i(JIT_V0, JIT_V0, 1);
jit_str_i((int)(data + arg), JIT_V0);
cip++;
break;
case OP_DEC_S: /* offset */
/* [FRM + offset] = [FRM + offset] - 1 */
jit_addi_i(JIT_V0, JIT_FP, arg);
jit_ldr_i(JIT_V1, JIT_V0);
jit_subi_i(JIT_V1, JIT_V1, 1);
jit_str_i(JIT_V0, JIT_V1);
cip++;
break;
case OP_DEC_I:
/* [PRI] = [PRI] - 1 */
jit_ldr_i(JIT_V0, JIT_R0);
jit_subi_i(JIT_V0, JIT_V0, 1);
jit_str_i(JIT_R0, JIT_V0);
break;
case OP_MOVS: /* number */
/* Copy memory from [PRI] to [ALT]. The parameter
* specifies the number of bytes. The blocks should not
* overlap.
*/
/*NOT_IMPLEMENTED("MOVS");*/
cip++;
break;
case OP_CMPS: /* number */
/* Compare memory blocks at [PRI] and [ALT]. The parameter
* specifies the number of bytes. The blocks should not
* overlap.
*/
NOT_IMPLEMENTED("CMPS");
break;
case OP_FILL: {
/* Fill memory at [ALT] with value in [PRI]. The parameter
* specifies the number of bytes, which must be a multiple
* of the cell size.
*/
jit_insn *loop;
jit_addi_i(JIT_V0, JIT_R1, (int)data + arg); /* V0 = R1 + data + numbytes */
jit_addi_i(JIT_V1, JIT_R1, (int)data); /* V1 = R1 + data */
loop = jit_bger_i(jit_forward(), JIT_V1, JIT_V0); /* LOOP: if (V1 >= V0) go to END */
jit_str_i(JIT_V1, JIT_R0); /* [V1] = R0 */
jit_addi_i(JIT_V1, JIT_V1, sizeof(cell)); /* V1 = V1 + sizeof(cell) */
jit_jmpi(loop); /* go to LOOP */
jit_patch(loop); /* END: */
cip++;
break;
}
case OP_HALT: /* number */
/* Abort execution (exit value in PRI), parameters other than 0
* have a special meaning.
*/
NOT_IMPLEMENTED("HALT");
break;
case OP_BOUNDS: /* value */
/* Abort execution if PRI > value or if PRI < 0 */
NOT_IMPLEMENTED("BOUNDS");
break;
case OP_SYSREQ_PRI:
/* call system service, service number in PRI */
NOT_IMPLEMENTED("SYSREQ.pri");
break;
case OP_SYSREQ_C: /* value */
/* call system service */
jit_prepare(2);
jit_movr_i(JIT_V0, JIT_SP);
jit_pusharg_i(JIT_V0);
jit_movi_i(JIT_V0, (int)amx);
jit_pusharg_i(JIT_V0);
jit_calli(get_native_address(amx, arg));
cip++;
break;
case OP_FILE: /* size ord name */
/* obsolete */
break;
case OP_LINE: /* line ord */
/* obsolete */
break;
case OP_SYMBOL: /* size offset flag name */
/* obsolete */
break;
case OP_SRANGE: /* level size */
/* obsolete */
break;
case OP_JUMP_PRI:
/* obsolete */
break;
case OP_SWITCH: /* offset */
/* Compare PRI to the values in the case table (whose address
* is passed as an offset from CIP) and jump to the associated
* the address in the matching record.
*/
NOT_IMPLEMENTED("SWITCH");
break;
case OP_CASETBL: /* ... */
/* A variable number of case records follows this opcode, where
* each record takes two cells. See the notes below for details
* on the case table lay-out.
*/
NOT_IMPLEMENTED("CASETBL");
break;
case OP_SWAP_PRI:
/* [STK] = PRI and PRI = [STK] */
jit_movr_i(JIT_V0, JIT_R0);
jit_ldr_i(JIT_R0, JIT_SP);
jit_str_i(JIT_SP, JIT_V0);
break;
case OP_SWAP_ALT:
/* [STK] = ALT and ALT = [STK] */
jit_movr_i(JIT_V0, JIT_R1);
jit_ldr_i(JIT_R0, JIT_SP);
jit_str_i(JIT_SP, JIT_V0);
break;
case OP_PUSHADDR: /* offset */
/* [STK] = FRM + offset, STK = STK - cell size */
jit_addi_i(JIT_V0, JIT_FP, arg);
jit_str_i(JIT_SP, JIT_V0);
jit_subr_i(JIT_SP, JIT_SP, sizeof(int));
cip++;
break;
case OP_NOP:
/* no-operation, for code alignment */
break;
case OP_SYSREQ_D: /* address */
/* call system service */
NOT_IMPLEMENTED("SYSREQ.D");
break;
case OP_SYMTAG: /* value */
/* obsolete */
break;
case OP_BREAK:
/* conditional breakpoint */
NOT_IMPLEMENTED("BREAK");
break;
default:
logprintf("opcode = %x, code = %x, cip = %x, code_size = %x", opcode, code, cip, code_size);
assert(0);
}
}
if (fn_list != NULL) {
struct list *cur_fn = fn_list;
struct list *cur_ref;
jit_insn *ip;
/* Save current instruction pointer. */
ip = (jit_insn*)jit_get_ip().ptr;
while (cur_fn != NULL) {
fn = (struct fn_info*)cur_fn->value;
assert(fn->jit_address != NULL);
/* Find all references to this funciton and replace the
* empty (null) address with the real one.
*/
cur_ref = fn->refs;
while (cur_ref != NULL) {
/*jit_set_ip((jit_insn*)fn->jit_address);
jit_patch((jit_insn*)(cur_ref->key));*/
jit_set_ip((jit_insn*)(cur_ref->key));
if (fn->is_public) {
jit_calli((int)fn->jit_address);
} else {
jit_jmpi((int)fn->jit_address);
}
cur_ref = cur_ref->next;
}
list_free(&fn->refs);
cur_fn = cur_fn->next;
}
/* Restore IP. */
jit_set_ip(ip);
}
jit_flush_code(jitcode, jit_get_ip().ptr);
list_free(&fn_list);
free(amx2jit);
dump_jit_code("jitcode.bin", jitcode, (int)jit_get_ip().ptr - (int)jitcode);
}
#if defined WIN32
#define PAGE_ALIGN(x) (x)
#define PROT_NONE 0x0 /* page can be executed */
#define PROT_READ 0x1 /* page can be read */
#define PROT_WRITE 0x2 /* page can be written */
#define PROT_EXEC 0x4 /* page can't be accessed */
static int mprotect(void *addr, size_t len, int prot) {
DWORD new_prot;
DWORD old_prot;
if (prot == PROT_NONE) {
new_prot = PAGE_NOACCESS;
} else if (prot == PROT_READ) {
new_prot = PAGE_READONLY;
} else if ((prot & PROT_WRITE) && (prot & PROT_READ)) {
new_prot = PAGE_EXECUTE_READWRITE;
} else if ((prot & PROT_READ)) {
new_prot = PAGE_EXECUTE_READ;
} else if ((prot & PROT_EXEC)) {
new_prot = PAGE_EXECUTE;
}
return !VirtualProtect((LPVOID)addr, (SIZE_T)len, new_prot, &old_prot);
}
#else
/* Linux alread has mprotect() */
#define PAGE_ALIGN(x) (void*)(((int)x + sysconf(_SC_PAGESIZE) - 1) & ~(sysconf(_SC_PAGESIZE) - 1))
#endif
typedef int (__stdcall *jitted_public)();
static int AMXAPI amx_Exec_JIT(AMX *amx, cell *retval, int index) {
struct amx_info *info;
jitted_public function;
info = list_get(amx_list, (int)amx);
if (info == NULL) {
/* store this AMX in the global list */
info = malloc(sizeof(*info));
info->jit_code_size = MAX_JIT_SIZE;
info->jit_code = malloc(sizeof(jit_insn) * info->jit_code_size);
list_insert(&amx_list, (int)amx, info);
/* JIT the code */
jit_amx(amx, info->jit_code, info->jit_code_size);
}
if (index == AMX_EXEC_MAIN) {
function = (jitted_public)((AMX_HEADER*)amx->base)->cod;
} else if (index >= 0) {
function = (jitted_public)get_public_address(amx, index);
}
function();
return AMX_ERR_NONE;
}
static void install_jmp_hook(void *from, void *to) {
/* set write permission */
mprotect(PAGE_ALIGN(from), PAGE_ALIGN(5), PROT_READ | PROT_WRITE | PROT_EXEC);
/* write the JMP opcode */
*((unsigned char*)from) = 0xE9;
/* write the address (relative to the nex instruction) */
*((int*)((int)from + 1)) = ((int)to - ((int)from + 5));
}
#define SUPPORTS_VERSION 0x0200
#define SUPPORTS_AMX_NATIVES 0x10000
PLUGIN_EXPORT unsigned int PLUGIN_CALL Supports() {
return SUPPORTS_VERSION | SUPPORTS_AMX_NATIVES;
}
#define PLUGIN_DATA_LOGPRINTF 0x00
#define PLUGIN_DATA_AXM_EXPORTS 0x10
#define PLUGIN_AMX_EXPORT_EXEC 7
PLUGIN_EXPORT bool PLUGIN_CALL Load(void **ppData) {
logprintf = (logprintf_t)ppData[PLUGIN_DATA_LOGPRINTF];
/* redirect amx_Exec() calls to amx_Exec_JIT() */
install_jmp_hook(((void**)ppData[PLUGIN_DATA_AXM_EXPORTS])[PLUGIN_AMX_EXPORT_EXEC],
(void*)amx_Exec_JIT);
return true;
}
PLUGIN_EXPORT void PLUGIN_CALL Unload() {
/* nothing */
}
PLUGIN_EXPORT int PLUGIN_CALL AmxLoad(AMX *amx) {
return AMX_ERR_NONE;
}
PLUGIN_EXPORT int PLUGIN_CALL AmxUnload(AMX *amx) {
struct amx_info *info;
info = (struct amx_info*)list_remove(amx_list, (int)amx);
free(info->jit_code);
free(info);
return AMX_ERR_NONE;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment