281 lines
7.7 KiB
C
281 lines
7.7 KiB
C
/* vim: ts=4 sw=4 sts=4 et tw=78
|
|
* Copyright (c) 2011 James R. McKaskill. See license in ffi.h
|
|
*/
|
|
#include "ffi.h"
|
|
|
|
static cfunction compile(Dst_DECL, lua_State* L, cfunction func, int ref);
|
|
|
|
static void* reserve_code(struct jit* jit, lua_State* L, size_t sz);
|
|
static void commit_code(struct jit* jit, void* p, size_t sz);
|
|
|
|
static void push_int(lua_State* L, int val)
|
|
{ lua_pushnumber(L, val); }
|
|
|
|
static void push_uint(lua_State* L, unsigned int val)
|
|
{ lua_pushnumber(L, val); }
|
|
|
|
static void push_float(lua_State* L, float val)
|
|
{ lua_pushnumber(L, val); }
|
|
|
|
#ifndef _WIN32
|
|
static int GetLastError(void)
|
|
{ return errno; }
|
|
static void SetLastError(int err)
|
|
{ errno = err; }
|
|
#endif
|
|
|
|
#ifdef NDEBUG
|
|
#define shred(a,b,c)
|
|
#else
|
|
#define shred(p,s,e) memset((uint8_t*)(p)+(s),0xCC,(e)-(s))
|
|
#endif
|
|
|
|
|
|
#ifdef _WIN64
|
|
#include "dynasm/dasm_x86.h"
|
|
#include "call_x64win.h"
|
|
#elif defined __amd64__
|
|
#include "dynasm/dasm_x86.h"
|
|
#include "call_x64.h"
|
|
#elif defined __arm__ || defined __arm || defined __ARM__ || defined __ARM || defined ARM || defined _ARM_ || defined ARMV4I || defined _M_ARM
|
|
#include "dynasm/dasm_arm.h"
|
|
#include "call_arm.h"
|
|
#else
|
|
#include "dynasm/dasm_x86.h"
|
|
#include "call_x86.h"
|
|
#endif
|
|
|
|
struct jit_head {
|
|
size_t size;
|
|
int ref;
|
|
uint8_t jump[JUMP_SIZE];
|
|
};
|
|
|
|
#define LINKTABLE_MAX_SIZE (sizeof(extnames) / sizeof(extnames[0]) * (JUMP_SIZE))
|
|
|
|
static cfunction compile(struct jit* jit, lua_State* L, cfunction func, int ref)
|
|
{
|
|
struct jit_head* code;
|
|
size_t codesz;
|
|
int err;
|
|
|
|
dasm_checkstep(jit, -1);
|
|
if ((err = dasm_link(jit, &codesz)) != 0) {
|
|
char buf[32];
|
|
sprintf(buf, "%x", err);
|
|
luaL_error(L, "dasm_link error %s", buf);
|
|
}
|
|
|
|
codesz += sizeof(struct jit_head);
|
|
code = (struct jit_head*) reserve_code(jit, L, codesz);
|
|
code->ref = ref;
|
|
code->size = codesz;
|
|
compile_extern_jump(jit, L, func, code->jump);
|
|
|
|
if ((err = dasm_encode(jit, code+1)) != 0) {
|
|
char buf[32];
|
|
sprintf(buf, "%x", err);
|
|
commit_code(jit, code, 0);
|
|
luaL_error(L, "dasm_encode error %s", buf);
|
|
}
|
|
|
|
commit_code(jit, code, codesz);
|
|
return (cfunction) (code+1);
|
|
}
|
|
|
|
typedef uint8_t jump_t[JUMP_SIZE];
|
|
|
|
int get_extern(struct jit* jit, uint8_t* addr, int idx, int type)
|
|
{
|
|
struct page* page = jit->pages[jit->pagenum-1];
|
|
jump_t* jumps = (jump_t*) (page+1);
|
|
struct jit_head* h = (struct jit_head*) ((uint8_t*) page + page->off);
|
|
uint8_t* jmp;
|
|
ptrdiff_t off;
|
|
|
|
if (idx == jit->function_extern) {
|
|
jmp = h->jump;
|
|
} else {
|
|
jmp = jumps[idx];
|
|
}
|
|
|
|
/* compensate for room taken up for the offset so that we can work rip
|
|
* relative */
|
|
addr += BRANCH_OFF;
|
|
|
|
/* see if we can fit the offset in the branch displacement, if not use the
|
|
* jump instruction */
|
|
off = *(uint8_t**) jmp - addr;
|
|
|
|
if (MIN_BRANCH <= off && off <= MAX_BRANCH) {
|
|
return (int32_t) off;
|
|
} else {
|
|
return (int32_t)(jmp + sizeof(uint8_t*) - addr);
|
|
}
|
|
}
|
|
|
|
#if LUA_VERSION_NUM >= 503
|
|
LUA_API void lua_remove_compat (lua_State *L, int idx) {
|
|
lua_remove(L, idx);
|
|
}
|
|
#endif
|
|
|
|
static void* reserve_code(struct jit* jit, lua_State* L, size_t sz)
|
|
{
|
|
struct page* page;
|
|
size_t off = (jit->pagenum > 0) ? jit->pages[jit->pagenum-1]->off : 0;
|
|
size_t size = (jit->pagenum > 0) ? jit->pages[jit->pagenum-1]->size : 0;
|
|
|
|
if (off + sz >= size) {
|
|
int i;
|
|
uint8_t* pdata;
|
|
cfunction func;
|
|
|
|
/* need to create a new page */
|
|
jit->pages = (struct page**) realloc(jit->pages, (++jit->pagenum) * sizeof(jit->pages[0]));
|
|
|
|
size = ALIGN_UP(sz + LINKTABLE_MAX_SIZE + sizeof(struct page), jit->align_page_size);
|
|
|
|
page = (struct page*) AllocPage(size);
|
|
jit->pages[jit->pagenum-1] = page;
|
|
pdata = (uint8_t*) page;
|
|
page->size = size;
|
|
page->off = sizeof(struct page);
|
|
|
|
lua_newtable(L);
|
|
|
|
#define ADDFUNC(DLL, NAME) \
|
|
lua_pushliteral(L, #NAME); \
|
|
func = DLL ? (cfunction) GetProcAddressA(DLL, #NAME) : NULL; \
|
|
func = func ? func : (cfunction) &NAME; \
|
|
lua_pushcfunction(L, (lua_CFunction) func); \
|
|
lua_rawset(L, -3)
|
|
|
|
ADDFUNC(NULL, check_double);
|
|
ADDFUNC(NULL, check_float);
|
|
ADDFUNC(NULL, check_uint64);
|
|
ADDFUNC(NULL, check_int64);
|
|
ADDFUNC(NULL, check_int32);
|
|
ADDFUNC(NULL, check_uint32);
|
|
ADDFUNC(NULL, check_uintptr);
|
|
ADDFUNC(NULL, check_enum);
|
|
ADDFUNC(NULL, check_typed_pointer);
|
|
ADDFUNC(NULL, check_typed_cfunction);
|
|
ADDFUNC(NULL, check_complex_double);
|
|
ADDFUNC(NULL, check_complex_float);
|
|
ADDFUNC(NULL, unpack_varargs_stack);
|
|
ADDFUNC(NULL, unpack_varargs_stack_skip);
|
|
ADDFUNC(NULL, unpack_varargs_reg);
|
|
ADDFUNC(NULL, unpack_varargs_float);
|
|
ADDFUNC(NULL, unpack_varargs_int);
|
|
ADDFUNC(NULL, push_cdata);
|
|
ADDFUNC(NULL, push_int);
|
|
ADDFUNC(NULL, push_uint);
|
|
ADDFUNC(NULL, push_float);
|
|
ADDFUNC(jit->kernel32_dll, SetLastError);
|
|
ADDFUNC(jit->kernel32_dll, GetLastError);
|
|
ADDFUNC(jit->lua_dll, luaL_error);
|
|
ADDFUNC(jit->lua_dll, lua_pushnumber);
|
|
ADDFUNC(jit->lua_dll, lua_pushboolean);
|
|
ADDFUNC(jit->lua_dll, lua_gettop);
|
|
ADDFUNC(jit->lua_dll, lua_rawgeti);
|
|
ADDFUNC(jit->lua_dll, lua_pushnil);
|
|
ADDFUNC(jit->lua_dll, lua_callk);
|
|
ADDFUNC(jit->lua_dll, lua_settop);
|
|
#if LUA_VERSION_NUM >= 503
|
|
lua_pushliteral(L, "lua_remove");
|
|
lua_pushcfunction(L, (lua_CFunction) lua_remove_compat);
|
|
lua_rawset(L, -3);
|
|
#else
|
|
ADDFUNC(jit->lua_dll, lua_remove);
|
|
#endif
|
|
#undef ADDFUNC
|
|
|
|
for (i = 0; extnames[i] != NULL; i++) {
|
|
|
|
if (strcmp(extnames[i], "FUNCTION") == 0) {
|
|
shred(pdata + page->off, 0, JUMP_SIZE);
|
|
jit->function_extern = i;
|
|
|
|
} else {
|
|
lua_getfield(L, -1, extnames[i]);
|
|
func = (cfunction) lua_tocfunction(L, -1);
|
|
|
|
if (func == NULL) {
|
|
luaL_error(L, "internal error: missing link for %s", extnames[i]);
|
|
}
|
|
|
|
compile_extern_jump(jit, L, func, pdata + page->off);
|
|
lua_pop(L, 1);
|
|
}
|
|
|
|
page->off += JUMP_SIZE;
|
|
}
|
|
|
|
page->freed = page->off;
|
|
lua_pop(L, 1);
|
|
|
|
} else {
|
|
page = jit->pages[jit->pagenum-1];
|
|
EnableWrite(page, page->size);
|
|
}
|
|
|
|
return (uint8_t*) page + page->off;
|
|
}
|
|
|
|
static void commit_code(struct jit* jit, void* code, size_t sz)
|
|
{
|
|
struct page* page = jit->pages[jit->pagenum-1];
|
|
page->off += sz;
|
|
EnableExecute(page, page->size);
|
|
{
|
|
#if 0
|
|
FILE* out = fopen("\\Hard Disk\\out.bin", "wb");
|
|
fwrite(page, page->off, 1, out);
|
|
fclose(out);
|
|
#endif
|
|
}
|
|
}
|
|
|
|
/* push_func_ref pushes a copy of the upval table embedded in the compiled
|
|
* function func.
|
|
*/
|
|
void push_func_ref(lua_State* L, cfunction func)
|
|
{
|
|
struct jit_head* h = ((struct jit_head*) func) - 1;
|
|
lua_rawgeti(L, LUA_REGISTRYINDEX, h->ref);
|
|
}
|
|
|
|
void free_code(struct jit* jit, lua_State* L, cfunction func)
|
|
{
|
|
size_t i;
|
|
struct jit_head* h = ((struct jit_head*) func) - 1;
|
|
for (i = 0; i < jit->pagenum; i++) {
|
|
struct page* p = jit->pages[i];
|
|
|
|
if ((uint8_t*) h < (uint8_t*) p || (uint8_t*) p + p->size <= (uint8_t*) h) {
|
|
continue;
|
|
}
|
|
|
|
luaL_unref(L, LUA_REGISTRYINDEX, h->ref);
|
|
|
|
EnableWrite(p, p->size);
|
|
p->freed += h->size;
|
|
|
|
shred(h, 0, h->size);
|
|
|
|
if (p->freed < p->off) {
|
|
EnableExecute(p, p->size);
|
|
return;
|
|
}
|
|
|
|
FreePage(p, p->size);
|
|
memmove(&jit->pages[i], &jit->pages[i+1], (jit->pagenum - (i+1)) * sizeof(jit->pages[0]));
|
|
jit->pagenum--;
|
|
return;
|
|
}
|
|
|
|
assert(!"couldn't find func in the jit pages");
|
|
}
|
|
|