v4k-git-backup/tools/luaffi/call_arm.dasc

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);
}