640 lines
17 KiB
Plaintext
640 lines
17 KiB
Plaintext
/* vim: ts=4 sw=4 sts=4 et tw=78
|
|
* Copyright (c) 2011 James R. McKaskill. See license in ffi.h
|
|
*/
|
|
|.arch arm
|
|
|.actionlist build_actionlist
|
|
|.globalnames globnames
|
|
|.externnames extnames
|
|
|
|
#define JUMP_SIZE 8
|
|
#define MIN_BRANCH ((INT32_MIN) >> 8)
|
|
#define MAX_BRANCH ((INT32_MAX) >> 8)
|
|
#define BRANCH_OFF 4
|
|
|
|
static void compile_extern_jump(struct jit* jit, lua_State* L, function_t func, uint8_t* code)
|
|
{
|
|
/* The jump code is the function pointer followed by a stub to call the
|
|
* function pointer. The stub exists so we can jump to functions with an
|
|
* offset greater than 32MB.
|
|
*
|
|
* Note we have to manually set this up since there are commands buffered
|
|
* in the jit state.
|
|
*/
|
|
*(function_t*) code = func;
|
|
/* ldr pc, [pc - 12] */
|
|
*(uint32_t*) &code[4] = 0xE51FF00CU;
|
|
}
|
|
|
|
|.define TOP, r4
|
|
|.define L_ARG, r5
|
|
|.define DATA, r6
|
|
|.define DATA2, r7
|
|
|
|
|.macro load32, reg, val
|
|
| ldr reg, [pc]
|
|
| b >5
|
|
|.long val
|
|
|5:
|
|
|.endmacro
|
|
|
|
|.macro lcall, func
|
|
| mov r0, L_ARG
|
|
| bl func
|
|
|.endmacro
|
|
|
|
void compile_globals(struct jit* jit, lua_State* L)
|
|
{
|
|
(void) jit;
|
|
}
|
|
|
|
function_t push_callback(struct jit* jit, lua_State* L, int fidx, int ct_usr, const struct ctype* ct)
|
|
{
|
|
struct jit* Dst = jit;
|
|
int i, nargs, num_upvals, ref;
|
|
const struct ctype* mt;
|
|
|
|
int top = lua_gettop(L);
|
|
|
|
ct_usr = lua_absindex(L, ct_usr);
|
|
fidx = lua_absindex(L, fidx);
|
|
nargs = (int) lua_rawlen(L, ct_usr);
|
|
|
|
dasm_setup(Dst, build_actionlist);
|
|
|
|
lua_newtable(L);
|
|
lua_pushvalue(L, -1);
|
|
ref = luaL_ref(L, LUA_REGISTRYINDEX);
|
|
num_upvals = 0;
|
|
|
|
if (ct->has_var_arg) {
|
|
luaL_error(L, "can't create callbacks with varargs");
|
|
}
|
|
|
|
/* prolog and get the upval table */
|
|
| mov r12, sp
|
|
| push {r0, r1, r2, r3} // do this first so that r0-r3 is right before stack bound arguments
|
|
| push {TOP, L_ARG, DATA, DATA2, r12, lr}
|
|
| sub DATA, r12, #16 // points to r0 on stack
|
|
| ldr L_ARG, [pc, #8]
|
|
| ldr r2, [pc, #8]
|
|
| ldr r1, [pc, #8]
|
|
| b >1
|
|
|.long L, ref, LUA_REGISTRYINDEX
|
|
|1:
|
|
| lcall extern lua_rawgeti
|
|
|
|
/* get the lua function */
|
|
lua_pushvalue(L, fidx);
|
|
lua_rawseti(L, -2, ++num_upvals);
|
|
| mov r2, #num_upvals
|
|
| mvn r1, #0 // -1
|
|
| lcall extern lua_rawgeti
|
|
|
|
for (i = 1; i <= nargs; i++) {
|
|
lua_rawgeti(L, ct_usr, i);
|
|
mt = (const struct ctype*) lua_touserdata(L, -1);
|
|
|
|
if (mt->pointers) {
|
|
lua_getuservalue(L, -1);
|
|
lua_rawseti(L, -3, ++num_upvals); /* usr value */
|
|
lua_rawseti(L, -2, ++num_upvals); /* mt */
|
|
|
|
| mov r2, #num_upvals-1 // usr value
|
|
| mvn r1, #i // -i-1, stack is upval table, func, i-1 args
|
|
| lcall extern lua_rawgeti
|
|
| load32 r2, mt
|
|
| mvn r1, #0 // -1
|
|
| lcall extern push_cdata
|
|
| ldr r2, [DATA], #4
|
|
| str r2, [r0]
|
|
| mvn r1, #1 // -2
|
|
| lcall extern lua_remove // remove the usr value
|
|
|
|
} else {
|
|
switch (mt->type) {
|
|
case INT64_TYPE:
|
|
case UINT64_TYPE:
|
|
lua_rawseti(L, -2, ++num_upvals); /* mt */
|
|
| lcall extern lua_pushnil
|
|
| load32 r2, mt
|
|
| mvn r1, #0 // -1
|
|
| lcall extern push_cdata
|
|
| ldr r2, [DATA], #4
|
|
| ldr r3, [DATA], #4
|
|
| str r2, [r0]
|
|
| str r3, [r0, #4]
|
|
| mvn r1, #1 // -2
|
|
| lcall extern lua_remove // remove the nil usr
|
|
break;
|
|
|
|
case UINTPTR_TYPE:
|
|
lua_rawseti(L, -2, ++num_upvals); /* mt */
|
|
| lcall extern lua_pushnil
|
|
| load32 r2, mt
|
|
| mvn r1, #0 // -1
|
|
| lcall extern push_cdata
|
|
| ldr r2, [DATA], #4
|
|
| str r2, [r0]
|
|
| mvn r1, #1 // -2
|
|
| lcall extern lua_remove // remove the nil usr
|
|
break;
|
|
|
|
case BOOL_TYPE:
|
|
lua_pop(L, 1);
|
|
| ldr r1, [DATA], #4
|
|
| lcall extern lua_pushboolean
|
|
break;
|
|
|
|
case INT8_TYPE:
|
|
lua_pop(L, 1);
|
|
| ldr r1, [DATA], #4
|
|
| mov r1, r1, lsl #24
|
|
| mov r1, r1, asr #24
|
|
| lcall extern push_int
|
|
break;
|
|
|
|
case UINT8_TYPE:
|
|
lua_pop(L, 1);
|
|
| ldr r1, [DATA], #4
|
|
| and r1, r1, #0xFF
|
|
| lcall extern push_uint
|
|
break;
|
|
|
|
case INT16_TYPE:
|
|
lua_pop(L, 1);
|
|
| ldr r1, [DATA], #4
|
|
| mov r1, r1, lsl #16
|
|
| mov r1, r1, asr #16
|
|
| lcall extern push_int
|
|
break;
|
|
|
|
case UINT16_TYPE:
|
|
lua_pop(L, 1);
|
|
| ldr r1, [DATA], #4
|
|
| mov r1, r1, lsl #16
|
|
| mov r1, r1, lsr #16
|
|
| lcall extern push_uint
|
|
break;
|
|
|
|
case ENUM_TYPE:
|
|
case INT32_TYPE:
|
|
lua_pop(L, 1);
|
|
| ldr r1, [DATA], #4
|
|
| lcall extern push_int
|
|
break;
|
|
|
|
case UINT32_TYPE:
|
|
lua_pop(L, 1);
|
|
| ldr r1, [DATA], #4
|
|
| lcall extern push_uint
|
|
break;
|
|
|
|
case FLOAT_TYPE:
|
|
lua_pop(L, 1);
|
|
| ldr r1, [DATA], #4
|
|
| lcall extern push_float
|
|
break;
|
|
|
|
case DOUBLE_TYPE:
|
|
lua_pop(L, 1);
|
|
| ldmia DATA!, {r1, r2}
|
|
| lcall extern lua_pushnumber
|
|
break;
|
|
|
|
default:
|
|
luaL_error(L, "NYI: callback arg type");
|
|
}
|
|
}
|
|
}
|
|
|
|
lua_rawgeti(L, ct_usr, 0);
|
|
mt = (const struct ctype*) lua_touserdata(L, -1);
|
|
|
|
| mov r3, #0
|
|
| mov r2, #((mt->pointers || mt->type != VOID_TYPE) ? 1 : 0)
|
|
| mov r1, #nargs
|
|
| lcall extern lua_callk
|
|
|
|
if (mt->pointers) {
|
|
lua_getuservalue(L, -1);
|
|
lua_rawseti(L, -3, ++num_upvals); /* usr value */
|
|
lua_rawseti(L, -2, ++num_upvals); /* mt */
|
|
|
|
| mov r2, #num_upvals-1 // usr value
|
|
| mvn r1, #1 // -2 stack is (upval table, ret val)
|
|
| lcall extern lua_rawgeti
|
|
| load32 r3, mt
|
|
| mov r2, #0 // -1 - ct_usr
|
|
| mvn r1, #1 // -2 - val
|
|
| lcall extern to_typed_pointer
|
|
| mov DATA, r0
|
|
| mvn r1, #3 // -4 - remove 3 (upval table, ret val, usr value)
|
|
| lcall extern lua_settop
|
|
| mov r0, DATA
|
|
} else {
|
|
switch (mt->type) {
|
|
case ENUM_TYPE:
|
|
lua_getuservalue(L, -1);
|
|
lua_rawseti(L, -3, ++num_upvals); /* usr value */
|
|
lua_rawseti(L, -2, ++num_upvals); /* mt */
|
|
|
|
| mov r2, #num_upvals-1 // usr value
|
|
| mvn r1, #1 // -2 stack is (upval table, ret val)
|
|
| lcall extern lua_rawgeti
|
|
| load32 r3, mt
|
|
| mvn r2, #0 // -1 - ct_usr
|
|
| mvn r1, #1 // -2 - val
|
|
| lcall extern to_enum
|
|
| mov DATA, r0
|
|
| mvn r1, #3 // -4 - remove 3 (upval table, ret val, usr value)
|
|
| lcall extern lua_settop
|
|
| mov r0, DATA
|
|
break;
|
|
|
|
case VOID_TYPE:
|
|
| mvn r1, #1 // -2
|
|
| lcall extern lua_settop
|
|
lua_pop(L, 1);
|
|
break;
|
|
|
|
case BOOL_TYPE:
|
|
case INT8_TYPE:
|
|
case INT16_TYPE:
|
|
case INT32_TYPE:
|
|
| mvn r1, #0 // -1
|
|
| lcall extern to_int32
|
|
goto single;
|
|
|
|
case UINT8_TYPE:
|
|
case UINT16_TYPE:
|
|
case UINT32_TYPE:
|
|
| mvn r1, #0 // -1
|
|
| lcall extern to_uint32
|
|
goto single;
|
|
|
|
case INT64_TYPE:
|
|
| mvn r1, #0 // -1
|
|
| lcall extern to_int64
|
|
goto dual;
|
|
|
|
case UINT64_TYPE:
|
|
| mvn r1, #0 // -1
|
|
| lcall extern to_uint64
|
|
goto dual;
|
|
|
|
case UINTPTR_TYPE:
|
|
| mvn r1, #0 // -1
|
|
| lcall extern to_uintptr
|
|
goto single;
|
|
|
|
case FLOAT_TYPE:
|
|
| mvn r1, #0 // -1
|
|
| lcall extern to_float
|
|
goto single;
|
|
|
|
case DOUBLE_TYPE:
|
|
| mvn r1, #0 // -1
|
|
| lcall extern to_double
|
|
goto dual;
|
|
|
|
single:
|
|
| mov DATA, r0
|
|
| mvn r1, #2 // -3
|
|
| lcall extern lua_settop
|
|
| mov r0, DATA
|
|
lua_pop(L, 1);
|
|
break;
|
|
|
|
dual:
|
|
| mov DATA, r0
|
|
| mov DATA2, r1
|
|
| mvn r1, #2 // -3
|
|
| lcall extern lua_settop
|
|
| mov r0, DATA
|
|
| mov r1, DATA2
|
|
lua_pop(L, 1);
|
|
break;
|
|
|
|
default:
|
|
luaL_error(L, "NYI: callback return type");
|
|
}
|
|
}
|
|
|
|
| ldmia sp, {TOP, L_ARG, DATA, DATA2, sp, pc}
|
|
|
|
lua_pop(L, 1); /* upval table - already in registry */
|
|
assert(lua_gettop(L) == top);
|
|
|
|
{
|
|
void* p;
|
|
struct ctype ft;
|
|
function_t func;
|
|
|
|
func = compile(jit, L, NULL, ref);
|
|
|
|
ft = *ct;
|
|
ft.is_jitted = 1;
|
|
p = push_cdata(L, ct_usr, &ft);
|
|
*(function_t*) p = func;
|
|
|
|
assert(lua_gettop(L) == top + 1);
|
|
|
|
return func;
|
|
}
|
|
}
|
|
|
|
void push_function(struct jit* jit, lua_State* L, function_t func, int ct_usr, const struct ctype* ct)
|
|
{
|
|
struct jit* Dst = jit;
|
|
int i, nargs, num_upvals;
|
|
const struct ctype* mt;
|
|
void* p;
|
|
|
|
int top = lua_gettop(L);
|
|
|
|
ct_usr = lua_absindex(L, ct_usr);
|
|
nargs = (int) lua_rawlen(L, ct_usr);
|
|
|
|
p = push_cdata(L, ct_usr, ct);
|
|
*(function_t*) p = func;
|
|
num_upvals = 1;
|
|
|
|
dasm_setup(Dst, build_actionlist);
|
|
|
|
| mov r12, sp
|
|
| push {r0}
|
|
| push {TOP, L_ARG, DATA, DATA2, r11, r12, lr}
|
|
| sub r11, r12, #4
|
|
| mov L_ARG, r0
|
|
| lcall extern lua_gettop
|
|
| mov TOP, r0
|
|
| cmp TOP, #nargs
|
|
| // these should really be in globals - but for some reason dynasm breaks when you do that
|
|
if (ct->has_var_arg) {
|
|
| bge >1
|
|
| load32 r1, "too few arguments"
|
|
| lcall extern luaL_error
|
|
|1:
|
|
} else {
|
|
| beq >1
|
|
| load32 r1, "incorrect number of arguments"
|
|
| lcall extern luaL_error
|
|
|1:
|
|
}
|
|
|
|
/* reserve enough stack space for all of the arguments (8 bytes per
|
|
* argument for double and maintains alignment). Add an extra 16 bytes so
|
|
* that the pop {r0, r1, r2, r3} doesn't clean out our stack frame */
|
|
| sub sp, sp, TOP, lsl #3
|
|
| sub sp, sp, #16
|
|
| mov DATA, sp
|
|
|
|
for (i = 1; i <= nargs; i++) {
|
|
lua_rawgeti(L, ct_usr, i);
|
|
mt = (const struct ctype*) lua_touserdata(L, -1);
|
|
|
|
if (mt->pointers || mt->type == FUNCTION_PTR_TYPE || mt->type == ENUM_TYPE) {
|
|
lua_getuservalue(L, -1);
|
|
num_upvals += 2;
|
|
|
|
| ldr r3, [pc, #4]
|
|
| ldr r2, [pc, #4]
|
|
| b >1
|
|
|.long mt, lua_upvalueindex(num_upvals)
|
|
|1:
|
|
| mov r1, #i
|
|
| mov r0, L_ARG
|
|
|
|
if (mt->pointers) {
|
|
| bl extern to_typed_pointer
|
|
} else if (mt->type == FUNCTION_PTR_TYPE) {
|
|
| bl extern to_typed_function
|
|
} else if (mt->type == ENUM_TYPE) {
|
|
| bl extern to_enum
|
|
}
|
|
|
|
| str r0, [DATA], #4
|
|
|
|
} else {
|
|
lua_pop(L, 1);
|
|
| mov r1, #i
|
|
|
|
switch (mt->type) {
|
|
case INT8_TYPE:
|
|
| lcall extern to_int32
|
|
| mov r0, r0, lsl #24
|
|
| mov r0, r0, asr #24
|
|
| str r0, [DATA], #4
|
|
break;
|
|
|
|
case INT16_TYPE:
|
|
| lcall extern to_int32
|
|
| mov r0, r0, lsl #16
|
|
| mov r0, r0, asr #16
|
|
| str r0, [DATA], #4
|
|
break;
|
|
|
|
case INT32_TYPE:
|
|
| lcall extern to_int32
|
|
| str r0, [DATA], #4
|
|
break;
|
|
|
|
case UINT8_TYPE:
|
|
| lcall extern to_uint32
|
|
| and r0, r0, #0xFF
|
|
| str r0, [DATA], #4
|
|
break;
|
|
|
|
case UINT16_TYPE:
|
|
| lcall extern to_uint32
|
|
| mov r0, r0, lsl #16
|
|
| mov r0, r0, lsr #16
|
|
| str r0, [DATA], #4
|
|
break;
|
|
|
|
case UINT32_TYPE:
|
|
| lcall extern to_uint32
|
|
| str r0, [DATA], #4
|
|
break;
|
|
|
|
case INT64_TYPE:
|
|
| lcall extern to_int64
|
|
| str r0, [DATA], #4
|
|
| str r1, [DATA], #4
|
|
break;
|
|
|
|
case UINT64_TYPE:
|
|
| lcall extern to_uint64
|
|
| str r0, [DATA], #4
|
|
| str r1, [DATA], #4
|
|
break;
|
|
|
|
case DOUBLE_TYPE:
|
|
| lcall extern to_double
|
|
| str r0, [DATA], #4
|
|
| str r1, [DATA], #4
|
|
break;
|
|
|
|
case UINTPTR_TYPE:
|
|
| lcall extern to_uintptr
|
|
| str r0, [DATA], #4
|
|
break;
|
|
|
|
case FLOAT_TYPE:
|
|
| lcall extern to_float
|
|
| str r0, [DATA], #4
|
|
break;
|
|
|
|
default:
|
|
luaL_error(L, "NYI: call arg type");
|
|
}
|
|
}
|
|
}
|
|
|
|
if (ct->has_var_arg) {
|
|
| mov r3, DATA
|
|
| mov r2, TOP
|
|
| mov r1, #nargs+1
|
|
| lcall extern unpack_varargs_stack
|
|
}
|
|
|
|
| load32 r0, &jit->last_errno
|
|
| ldr r0, [r0]
|
|
| bl extern SetLastError
|
|
|
|
| pop {r0, r1, r2, r3} // this pop is balanced with the sub sp, #16
|
|
| bl extern FUNCTION
|
|
|
|
|.macro get_errno
|
|
| bl extern GetLastError
|
|
| load32 r1, &jit->last_errno
|
|
| str r0, [r1]
|
|
|.endmacro
|
|
|
|
|.macro return
|
|
| ldmdb r11, {TOP, L_ARG, DATA, r11, sp, pc}
|
|
|.endmacro
|
|
|
|
lua_rawgeti(L, ct_usr, 0);
|
|
mt = (const struct ctype*) lua_touserdata(L, -1);
|
|
|
|
if (mt->pointers) {
|
|
lua_getuservalue(L, -1);
|
|
num_upvals += 2;
|
|
| mov DATA, r0
|
|
| get_errno
|
|
| ldr r2, [pc, #4]
|
|
| ldr r1, [pc, #4]
|
|
| b >1
|
|
|.long mt, lua_upvalueindex(num_upvals)
|
|
|1:
|
|
| lcall extern push_cdata
|
|
| str DATA, [r0]
|
|
| mov r0, #1
|
|
| return
|
|
|
|
} else {
|
|
switch (mt->type) {
|
|
case INT64_TYPE:
|
|
case UINT64_TYPE:
|
|
num_upvals++;
|
|
| mov DATA, r0
|
|
| mov DATA2, r1
|
|
| get_errno
|
|
| lcall extern lua_pushnil
|
|
| load32 r2, mt
|
|
| mvn r1, #0 // -1
|
|
| lcall extern push_cdata
|
|
| str DATA, [r0]
|
|
| str DATA2, [r0, #4]
|
|
| mov r0, #1
|
|
| return
|
|
break;
|
|
|
|
case UINTPTR_TYPE:
|
|
num_upvals++;
|
|
| mov DATA, r0
|
|
| get_errno
|
|
| lcall extern lua_pushnil
|
|
| load32 r2, mt
|
|
| mvn r1, #0 // -1
|
|
| lcall extern push_cdata
|
|
| str DATA, [r0]
|
|
| mov r0, #1
|
|
| return
|
|
break;
|
|
|
|
case VOID_TYPE:
|
|
lua_pop(L, 1);
|
|
| get_errno
|
|
| mov r0, #0
|
|
| return
|
|
break;
|
|
|
|
case BOOL_TYPE:
|
|
lua_pop(L, 1);
|
|
| mov DATA, r0
|
|
| get_errno
|
|
| mov r1, DATA
|
|
| lcall extern lua_pushboolean
|
|
| mov r0, #1
|
|
| return
|
|
break;
|
|
|
|
case INT8_TYPE:
|
|
case INT16_TYPE:
|
|
case INT32_TYPE:
|
|
case ENUM_TYPE:
|
|
lua_pop(L, 1);
|
|
| mov DATA, r0
|
|
| get_errno
|
|
| mov r1, DATA
|
|
| lcall extern push_int
|
|
| mov r0, #1
|
|
| return
|
|
break;
|
|
|
|
case UINT8_TYPE:
|
|
case UINT16_TYPE:
|
|
case UINT32_TYPE:
|
|
lua_pop(L, 1);
|
|
| mov DATA, r0
|
|
| get_errno
|
|
| mov r1, DATA
|
|
| lcall extern push_uint
|
|
| mov r0, #1
|
|
| return
|
|
break;
|
|
|
|
case FLOAT_TYPE:
|
|
lua_pop(L, 1);
|
|
| mov DATA, r0
|
|
| get_errno
|
|
| mov r1, DATA
|
|
| lcall extern push_float
|
|
| mov r0, #1
|
|
| return
|
|
break;
|
|
|
|
case DOUBLE_TYPE:
|
|
lua_pop(L, 1);
|
|
| mov DATA, r0
|
|
| mov DATA2, r1
|
|
| get_errno
|
|
| mov r2, DATA2
|
|
| mov r1, DATA
|
|
| lcall extern lua_pushnumber
|
|
| mov r0, #1
|
|
| return
|
|
break;
|
|
|
|
default:
|
|
luaL_error(L, "NYI: call return type");
|
|
}
|
|
}
|
|
|
|
assert(lua_gettop(L) == top + num_upvals);
|
|
lua_pushcclosure(L, (lua_CFunction) compile(jit, L, func, LUA_NOREF), num_upvals);
|
|
}
|
|
|