From fb0a18ca0bbc96aac105d903e1db92d31f1d4d3c Mon Sep 17 00:00:00 2001 From: Mike Pall Date: Thu, 6 Jan 2011 23:30:04 +0100 Subject: [PATCH] FFI: Add support for calling C functions. --- src/Makefile | 2 +- src/Makefile.dep | 24 +-- src/buildvm.c | 3 + src/buildvm_ppc.dasc | 9 ++ src/buildvm_x86.dasc | 95 ++++++++++++ src/lib_ffi.c | 8 +- src/lj_ccall.c | 349 +++++++++++++++++++++++++++++++++++++++++++ src/lj_ccall.h | 131 ++++++++++++++++ src/lj_ctype.h | 15 +- src/lj_errmsg.h | 4 +- src/ljamalg.c | 1 + 11 files changed, 617 insertions(+), 24 deletions(-) create mode 100644 src/lj_ccall.c create mode 100644 src/lj_ccall.h diff --git a/src/Makefile b/src/Makefile index ba94f2fa..b925b4e5 100644 --- a/src/Makefile +++ b/src/Makefile @@ -328,7 +328,7 @@ LJCORE_O= lj_gc.o lj_err.o lj_char.o lj_bc.o lj_obj.o \ lj_opt_dce.o lj_opt_loop.o \ lj_mcode.o lj_snap.o lj_record.o lj_crecord.o lj_ffrecord.o \ lj_asm.o lj_trace.o lj_gdbjit.o \ - lj_ctype.o lj_cdata.o lj_cconv.o lj_cparse.o \ + lj_ctype.o lj_cdata.o lj_cconv.o lj_ccall.o lj_cparse.o \ lj_lib.o lj_alloc.o lib_aux.o \ $(LJLIB_O) lib_init.o diff --git a/src/Makefile.dep b/src/Makefile.dep index f3a2e710..9b790fb8 100644 --- a/src/Makefile.dep +++ b/src/Makefile.dep @@ -1,6 +1,6 @@ buildvm.o: buildvm.c buildvm.h lj_def.h lua.h luaconf.h lj_arch.h \ lj_obj.h lj_gc.h lj_bc.h lj_ir.h lj_frame.h lj_dispatch.h lj_jit.h \ - luajit.h \ + lj_ccall.h luajit.h \ lj_traceerr.h buildvm_asm.o: buildvm_asm.c buildvm.h lj_def.h lua.h luaconf.h lj_arch.h \ lj_bc.h @@ -22,7 +22,7 @@ lib_debug.o: lib_debug.c lua.h luaconf.h lauxlib.h lualib.h lj_obj.h \ lj_def.h lj_arch.h lj_err.h lj_errmsg.h lj_lib.h lj_libdef.h lib_ffi.o: lib_ffi.c lua.h luaconf.h lauxlib.h lualib.h lj_obj.h lj_def.h \ lj_arch.h lj_gc.h lj_err.h lj_errmsg.h lj_str.h lj_ctype.h lj_cparse.h \ - lj_cdata.h lj_cconv.h lj_ff.h lj_ffdef.h lj_lib.h lj_libdef.h + lj_cdata.h lj_cconv.h lj_ccall.h lj_ff.h lj_ffdef.h lj_lib.h lj_libdef.h lib_init.o: lib_init.c lua.h luaconf.h lauxlib.h lualib.h lj_arch.h lib_io.o: lib_io.c lua.h luaconf.h lauxlib.h lualib.h lj_obj.h lj_def.h \ lj_arch.h lj_gc.h lj_err.h lj_errmsg.h lj_str.h lj_ff.h lj_ffdef.h \ @@ -55,6 +55,9 @@ lj_asm.o: lj_asm.c lj_obj.h lua.h luaconf.h lj_def.h lj_arch.h lj_gc.h \ lj_asm.h lj_vm.h lj_target.h lj_target_*.h lj_bc.o: lj_bc.c lj_obj.h lua.h luaconf.h lj_def.h lj_arch.h lj_bc.h \ lj_bcdef.h +lj_ccall.o: lj_ccall.c lj_obj.h lua.h luaconf.h lj_def.h lj_arch.h \ + lj_err.h lj_errmsg.h lj_str.h lj_ctype.h lj_gc.h lj_cconv.h lj_cdata.h \ + lj_ccall.h lj_cconv.o: lj_cconv.c lj_obj.h lua.h luaconf.h lj_def.h lj_arch.h \ lj_err.h lj_errmsg.h lj_tab.h lj_ctype.h lj_gc.h lj_cdata.h lj_cconv.h lj_cdata.o: lj_cdata.c lj_obj.h lua.h luaconf.h lj_def.h lj_arch.h \ @@ -153,12 +156,13 @@ ljamalg.o: ljamalg.c lua.h luaconf.h lauxlib.h lj_gc.c lj_obj.h lj_def.h \ lj_func.c lj_udata.c lj_meta.c lj_state.c lj_lex.h lj_alloc.h \ lj_dispatch.c lj_ff.h lj_ffdef.h luajit.h lj_vmevent.c lj_vmevent.h \ lj_api.c lj_parse.h lj_lex.c lualib.h lj_parse.c lj_ctype.c lj_cdata.c \ - lj_cconv.h lj_cconv.c lj_cparse.c lj_cparse.h lj_lib.c lj_lib.h lj_ir.c \ - lj_iropt.h lj_opt_mem.c lj_opt_fold.c lj_folddef.h lj_opt_narrow.c \ - lj_opt_dce.c lj_opt_loop.c lj_snap.h lj_mcode.c lj_mcode.h lj_snap.c \ - lj_target.h lj_target_*.h lj_record.c lj_record.h lj_ffrecord.h \ - lj_crecord.c lj_crecord.h lj_ffrecord.c lj_recdef.h lj_asm.c lj_asm.h \ - lj_trace.c lj_gdbjit.h lj_gdbjit.c lj_alloc.c lib_aux.c lib_base.c \ - lj_libdef.h lib_math.c lib_string.c lib_table.c lib_io.c lib_os.c \ - lib_package.c lib_debug.c lib_bit.c lib_jit.c lib_ffi.c lib_init.c + lj_cconv.h lj_cconv.c lj_ccall.c lj_ccall.h lj_cparse.c lj_cparse.h \ + lj_lib.c lj_lib.h lj_ir.c lj_iropt.h lj_opt_mem.c lj_opt_fold.c \ + lj_folddef.h lj_opt_narrow.c lj_opt_dce.c lj_opt_loop.c lj_snap.h \ + lj_mcode.c lj_mcode.h lj_snap.c lj_target.h lj_target_*.h lj_record.c \ + lj_record.h lj_ffrecord.h lj_crecord.c lj_crecord.h lj_ffrecord.c \ + lj_recdef.h lj_asm.c lj_asm.h lj_trace.c lj_gdbjit.h lj_gdbjit.c \ + lj_alloc.c lib_aux.c lib_base.c lj_libdef.h lib_math.c lib_string.c \ + lib_table.c lib_io.c lib_os.c lib_package.c lib_debug.c lib_bit.c \ + lib_jit.c lib_ffi.c lib_init.c luajit.o: luajit.c lua.h luaconf.h lauxlib.h lualib.h luajit.h lj_arch.h diff --git a/src/buildvm.c b/src/buildvm.c index 8c127d45..ddf4e0f0 100644 --- a/src/buildvm.c +++ b/src/buildvm.c @@ -21,6 +21,9 @@ #include "lj_ir.h" #include "lj_frame.h" #include "lj_dispatch.h" +#if LJ_HASFFI +#include "lj_ccall.h" +#endif #include "luajit.h" #if defined(_WIN32) diff --git a/src/buildvm_ppc.dasc b/src/buildvm_ppc.dasc index a81335db..22f6da55 100644 --- a/src/buildvm_ppc.dasc +++ b/src/buildvm_ppc.dasc @@ -2074,6 +2074,15 @@ static void build_subroutines(BuildCtx *ctx) |//----------------------------------------------------------------------- | |//----------------------------------------------------------------------- + |//-- FFI helper functions ----------------------------------------------- + |//----------------------------------------------------------------------- + | + |->vm_ffi_call: +#if LJ_HASFFI + | NYI +#endif + | + |//----------------------------------------------------------------------- } /* Generate the code for a single instruction. */ diff --git a/src/buildvm_x86.dasc b/src/buildvm_x86.dasc index b4f34782..31a09b7e 100644 --- a/src/buildvm_x86.dasc +++ b/src/buildvm_x86.dasc @@ -3409,6 +3409,101 @@ static void build_subroutines(BuildCtx *ctx, int cmov, int sse) |.endif | |//----------------------------------------------------------------------- + |//-- FFI helper functions ----------------------------------------------- + |//----------------------------------------------------------------------- + | + |->vm_ffi_call: +#if LJ_HASFFI + |.if X64 + | .type CCSTATE, CCallState, rbx + | push rbp; mov rbp, rsp; push rbx; mov CCSTATE, CARG1 + |.else + | .type CCSTATE, CCallState, ebx + | push ebp; mov ebp, esp; push ebx; mov CCSTATE, FCARG1 + |.endif + | + | // Readjust stack. + |.if X64 + | mov eax, CCSTATE->spadj + | sub rsp, rax + |.else + | sub esp, CCSTATE->spadj + |.endif + | + | // Copy stack slots. + | movzx ecx, byte CCSTATE->nsp + | sub ecx, 1 + | js >2 + |1: + |.if X64 + | mov rax, [CCSTATE+rcx*8+offsetof(CCallState, stack)] + | mov [rsp+rcx*8+CCALL_SPS_EXTRA*8], rax + |.else + | mov eax, [CCSTATE+ecx*4+offsetof(CCallState, stack)] + | mov [esp+ecx*4], eax + |.endif + | sub ecx, 1 + | jns <1 + |2: + | + |.if X64 + | movzx eax, byte CCSTATE->nfpr + | mov CARG1, CCSTATE->gpr[0] + | mov CARG2, CCSTATE->gpr[1] + | mov CARG3, CCSTATE->gpr[2] + | mov CARG4, CCSTATE->gpr[3] + |.if not X64WIN + | mov CARG5, CCSTATE->gpr[4] + | mov CARG6, CCSTATE->gpr[5] + |.endif + | test eax, eax; jz >5 + | movaps xmm0, CCSTATE->fpr[0] + | movaps xmm1, CCSTATE->fpr[1] + | movaps xmm2, CCSTATE->fpr[2] + | movaps xmm3, CCSTATE->fpr[3] + |.if not X64WIN + | cmp eax, 4; jbe >5 + | movaps xmm4, CCSTATE->fpr[4] + | movaps xmm5, CCSTATE->fpr[5] + | movaps xmm6, CCSTATE->fpr[6] + | movaps xmm7, CCSTATE->fpr[7] + |.endif + |5: + |.else + | mov FCARG1, CCSTATE->gpr[0] + | mov FCARG2, CCSTATE->gpr[1] + |.endif + | + | call aword CCSTATE->func + | + |.if X64 + | mov CCSTATE->gpr[0], rax + | movaps CCSTATE->fpr[0], xmm0 + |.if not X64WIN + | mov CCSTATE->gpr[1], rdx + | movaps CCSTATE->fpr[1], xmm1 + |.endif + |.else + | mov CCSTATE->gpr[0], eax + | mov CCSTATE->gpr[1], edx + | cmp byte CCSTATE->resx87, 1 + | jb >7 + | je >6 + | fstp qword CCSTATE->fpr[0].d[0] + | jmp >7 + |6: + | fstp dword CCSTATE->fpr[0].f[0] + |7: + |.endif + | + |.if X64 + | mov rbx, [rbp-8]; leave; ret + |.else + | mov ebx, [ebp-4]; leave; ret + |.endif +#endif + | + |//----------------------------------------------------------------------- } /* Generate the code for a single instruction. */ diff --git a/src/lib_ffi.c b/src/lib_ffi.c index 18a22ce1..c80f3fb6 100644 --- a/src/lib_ffi.c +++ b/src/lib_ffi.c @@ -21,6 +21,7 @@ #include "lj_cparse.h" #include "lj_cdata.h" #include "lj_cconv.h" +#include "lj_ccall.h" #include "lj_ff.h" #include "lj_lib.h" @@ -148,10 +149,13 @@ static int lj_cf_ffi_new(lua_State *L); LJLIB_CF(ffi_meta___call) LJLIB_REC(cdata_call) { GCcdata *cd = ffi_checkcdata(L, 1); + int ret; if (cd->typeid == CTID_CTYPEID) return lj_cf_ffi_new(L); - lj_err_caller(L, LJ_ERR_FFI_NYICALL); - return 0; /* unreachable */ + if ((ret = lj_ccall_func(L, cd)) < 0) + lj_err_callerv(L, LJ_ERR_FFI_BADCALL, + strdata(lj_ctype_repr(L, cd->typeid, NULL))); + return ret; } /* Pointer arithmetic. */ diff --git a/src/lj_ccall.c b/src/lj_ccall.c new file mode 100644 index 00000000..5e91d962 --- /dev/null +++ b/src/lj_ccall.c @@ -0,0 +1,349 @@ +/* +** FFI C call handling. +** Copyright (C) 2005-2010 Mike Pall. See Copyright Notice in luajit.h +*/ + +#include "lj_obj.h" + +#if LJ_HASFFI + +#include "lj_err.h" +#include "lj_str.h" +#include "lj_ctype.h" +#include "lj_cconv.h" +#include "lj_cdata.h" +#include "lj_ccall.h" + +/* Target-specific handling of register arguments. */ +#if LJ_TARGET_X86 + +#define CCALL_HANDLE_REGARG \ + if (!isfp) { /* Only non-FP values may be passed in registers. */ \ + if (n > 1) { /* Anything > 32 bit is passed on the stack. */ \ + ngpr = maxgpr; /* Prevent reordering. */ \ + } else if (ngpr + 1 <= maxgpr) { \ + dp = &cc->gpr[ngpr]; \ + ngpr += n; \ + goto done; \ + } \ + } + +#elif LJ_TARGET_X64 && LJ_ABI_WIN + +/* Windows/x64 argument registers are strictly positional (use ngpr). */ +#define CCALL_HANDLE_REGARG \ + if (isfp) { \ + if (ngpr < 4) { dp = &cc->fpr[ngpr++]; nfpr = ngpr; goto done; } \ + } else { \ + if (ngpr < 4) { dp = &cc->gpr[ngpr++]; goto done; } \ + } + +#elif LJ_TARGET_X64 + +#define CCALL_HANDLE_REGARG \ + if (isfp) { /* Try to pass argument in FPRs. */ \ + if (nfpr + isfp <= CCALL_NARG_FPR) { \ + dp = &cc->fpr[nfpr]; \ + nfpr += isfp; \ + goto done; \ + } \ + } else { /* Try to pass argument in GPRs. */ \ + /* Note that reordering is explicitly allowed in the x64 ABI. */ \ + if (n <= 2 && ngpr + n <= maxgpr) { \ + dp = &cc->gpr[ngpr]; \ + ngpr += n; \ + goto done; \ + } \ + } + +#elif LJ_TARGET_PPCSPE + +/* PPC/SPE has a softfp ABI. */ +#define CCALL_HANDLE_REGARG \ + if (n > 1) { /* Doesn't fit in a single GPR? */ \ + lua_assert(n == 2 || n == 4); /* int64_t, double or complex (float). */ \ + if (n == 2) \ + ngpr = (ngpr + 1u) & ~1u; /* Only align 64 bit value to regpair. */ \ + else if (ngpr + n > maxgpr) \ + ngpr = maxgpr; /* Prevent reordering. */ \ + } \ + if (ngpr + n <= maxgpr) { \ + dp = &cc->gpr[ngpr]; \ + ngpr += n; \ + goto done; \ + } + +#else +#error "missing definition for handling of register arguments" +#endif + +/* Infer the destination CTypeID for a vararg argument. */ +static CTypeID ccall_ctid_vararg(CTState *cts, cTValue *o) +{ + if (tvisnum(o)) { + return CTID_DOUBLE; + } else if (tviscdata(o)) { + CTypeID id = cdataV(o)->typeid; + CType *s = ctype_get(cts, id); + if (ctype_isrefarray(s->info)) { + return lj_ctype_intern(cts, + CTINFO(CT_PTR, CTALIGN_PTR|ctype_cid(s->info)), CTSIZE_PTR); + } else if (ctype_isstruct(s->info) || ctype_isfunc(s->info)) { + return lj_ctype_intern(cts, CTINFO(CT_PTR, CTALIGN_PTR|id), CTSIZE_PTR); + } if (ctype_isfp(s->info) && s->size == sizeof(float)) { + return CTID_DOUBLE; + } else { + return id; + } + } else if (tvisstr(o)) { + return CTID_P_CCHAR; + } else if (tvisbool(o)) { + return CTID_BOOL; + } else { + return CTID_P_VOID; + } +} + +/* Setup arguments for C call. */ +static void ccall_set_args(lua_State *L, CTState *cts, CType *ct, + CCallState *cc) +{ + TValue *o, *top = L->top; + CTypeID fid; + CType *ctr; + MSize maxgpr, ngpr = 0, nsp = 0; +#if CCALL_NARG_FPR + MSize nfpr = 0; +#endif + + /* Clear unused regs to get some determinism in case of misdeclaration. */ + memset(cc->gpr, 0, sizeof(cc->gpr)); +#if CCALL_NUM_FPR + memset(cc->fpr, 0, sizeof(cc->fpr)); +#endif + +#if LJ_TARGET_X86 + /* x86 has several different calling conventions. */ + cc->resx87 = 0; + switch ((ct->info >> CTSHIFT_CCONV) & CTMASK_CCONV) { + case CTCC_FASTCALL: maxgpr = 2; break; + case CTCC_THISCALL: maxgpr = 1; break; + default: maxgpr = 0; break; + } +#else + maxgpr = CCALL_NARG_GPR; +#endif + + /* Perform required setup for some result types. */ + ctr = ctype_rawchild(cts, ct); + if (ctype_isvector(ctr->info)) { + if (!(CCALL_VECTOR_REG && (ctr->size == 8 || ctr->size == 16))) + goto err_nyi; + } else if (ctype_iscomplex(ctr->info) || ctype_isstruct(ctr->info)) { + /* Preallocate cdata object and anchor it after arguments. */ + CTSize sz = ctr->size; + GCcdata *cd = lj_cdata_new(cts, ctype_cid(ct->info), sz); + setcdataV(L, L->top++, cd); + if (ctype_iscomplex(ctr->info)) { + cc->retref = (sz == 2*sizeof(float)) ? CCALL_COMPLEXF_RETREF : + CCALL_COMPLEX_RETREF; + } else { +#if CCALL_STRUCT_RETREF + cc->retref = 1; /* Return all structs by reference. */ +#elif LJ_TARGET_X64 +#if LJ_ABI_WIN + /* Return structs of size 1, 2, 4 or 8 in a GPR. */ + cc->retref = !(sz == 1 || sz == 2 || sz == 4 || sz == 8); +#else + if (sz <= 16) goto err_nyi; /* NYI: crazy x64 rules for structs. */ + cc->retref = 1; /* Return all bigger structs by reference. */ +#endif +#else +#error "missing definition for handling of struct return values" +#endif + } + /* Pass reference to returned aggregate in first argument. */ + if (cc->retref) { + if (ngpr < maxgpr) + cc->gpr[ngpr++] = (GPRArg)cdataptr(cd); + else + cc->stack[nsp++] = (GPRArg)cdataptr(cd); + } +#if LJ_TARGET_X86 + } else if (ctype_isfp(ctr->info)) { + cc->resx87 = ctr->size == sizeof(float) ? 1 : 2; +#endif + } + + /* Walk through all passed arguments. */ + for (fid = ct->sib, o = L->base+1; o < top; o++) { + CTypeID did; + CType *d; + CTSize sz; + MSize n, isfp = 0, isva = 0; + void *dp, *rp = NULL; + + if (fid) { /* Get argument type from field. */ + CType *ctf = ctype_get(cts, fid); + fid = ctf->sib; + lua_assert(ctype_isfield(ctf->info)); + did = ctype_cid(ctf->info); + } else { + if (!(ct->info & CTF_VARARG)) + lj_err_caller(L, LJ_ERR_FFI_NUMARG); /* Too many arguments. */ + did = ccall_ctid_vararg(cts, o); /* Infer vararg type. */ + isva = 1; + } + d = ctype_raw(cts, did); + sz = d->size; + + /* Find out how (by value/ref) and where (GPR/FPR) to pass an argument. */ + if (ctype_isnum(d->info)) { + if (sz > 8) goto err_nyi; + if ((d->info & CTF_FP)) { + isfp = 1; + } else if (sz < CTSIZE_PTR) { + d = ctype_get(cts, CTID_INT_PSZ); + } + } else if (ctype_isvector(d->info)) { + if (CCALL_VECTOR_REG && (sz == 8 || sz == 16)) + isfp = 1; + else + goto err_nyi; + } else if (ctype_iscomplex(d->info)) { +#if CCALL_COMPLEX_ARGREF + rp = cdataptr(lj_cdata_new(cts, did, sz)); + sz = CTSIZE_PTR; +#else + isfp = 2; +#endif + } else if (ctype_isstruct(d->info)) { + int sref = CCALL_STRUCT_ARGREF; +#if LJ_TARGET_X86 + ngpr = maxgpr; /* Pass all structs by value on the stack. */ +#elif LJ_TARGET_X64 +#if LJ_ABI_WIN + /* Pass structs of size 1, 2, 4 or 8 in a GPR by value. */ + sref = !(sz == 1 || sz == 2 || sz == 4 || sz == 8); +#else + if (sz <= 16) goto err_nyi; /* NYI: crazy x64 rules for structs. */ + /* Pass all bigger structs by value on the stack. */ +#endif +#endif + if (sref) { /* Pass struct by reference. */ + rp = cdataptr(lj_cdata_new(cts, did, sz)); + sz = CTSIZE_PTR; /* Pass all other structs by reference. */ + } + } else { + sz = CTSIZE_PTR; + } + sz = (sz + CTSIZE_PTR-1) & ~(CTSIZE_PTR-1); + n = sz / CTSIZE_PTR; /* Number of GPRs or stack slots needed. */ + + CCALL_HANDLE_REGARG /* Handle register arguments. */ + + /* Otherwise pass argument on stack. */ + if (CCALL_ALIGN_STACKARG && !rp && (d->info & CTF_ALIGN) > CTALIGN_PTR) { + MSize align = (1u << ctype_align(d->info-CTALIGN_PTR)) -1; + nsp = (nsp + align) & ~align; /* Align argument on stack. */ + } + if (nsp + n >= CCALL_MAXSTACK) { /* Too many arguments. */ + err_nyi: + lj_err_caller(L, LJ_ERR_FFI_NYICALL); + } + dp = &cc->stack[nsp]; + nsp += n; + isva = 0; + + done: + if (rp) { /* Pass by reference. */ + *(void **)dp = rp; + dp = rp; + } + lj_cconv_ct_tv(cts, d, (uint8_t *)dp, o, 0); +#if LJ_TARGET_X64 && LJ_ABI_WIN + if (isva) { /* Windows/x64 mirrors varargs in both register sets. */ + if (nfpr == ngpr) + cc->gpr[ngpr-1] = cc->fpr[ngpr-1].l[0]; + else + cc->fpr[ngpr-1].l[0] = cc->gpr[ngpr-1]; + } +#endif + } + if (fid) lj_err_caller(L, LJ_ERR_FFI_NUMARG); /* Too few arguments. */ + +#if LJ_TARGET_X64 + cc->nfpr = nfpr; /* Required for vararg functions. */ +#endif + cc->nsp = nsp; + cc->spadj = (CCALL_SPS_FREE + CCALL_SPS_EXTRA)*CTSIZE_PTR; + if (nsp > CCALL_SPS_FREE) + cc->spadj += (((nsp-CCALL_SPS_FREE)*CTSIZE_PTR + 15u) & ~15u); +} + +/* Get results from C call. */ +static int ccall_get_results(lua_State *L, CTState *cts, CType *ct, + CCallState *cc) +{ + CType *ctr = ctype_rawchild(cts, ct); + void *sp = &cc->gpr[0]; + if (ctype_isvoid(ctr->info)) + return 0; /* Zero results. */ + if (ctype_isstruct(ctr->info)) { + if (!CCALL_STRUCT_RETREF && !cc->retref) { + void *dp = cdataptr(cdataV(L->top-1)); /* Use preallocated object. */ + memcpy(dp, sp, ctr->size); /* Copy struct return value from GPRs. */ + } + return 1; /* Return cdata object which is already on top of stack. */ + } + if (ctype_iscomplex(ctr->info)) { +#if !CCALL_COMPLEX_RETREF || !CCALL_COMPLEXF_RETREF + void *dp = cdataptr(cdataV(L->top-1)); /* Use preallocated object. */ +#if CCALL_COMPLEX_RETREF && !CCALL_COMPLEXF_RETREF + if (ctr->size == 2*sizeof(float)) + memcpy(dp, sp, ctr->size); /* Copy complex float from GPRs. */ +#elif CCALL_NUM_FPR + /* Copy non-contiguous re/im part from FPRs to cdata object. */ + if (ctr->size == 2*sizeof(float)) { + ((float *)dp)[0] = cc->fpr[0].f[0]; + ((float *)dp)[1] = cc->fpr[1].f[0]; + } else { + ((double *)dp)[0] = cc->fpr[0].d[0]; + ((double *)dp)[1] = cc->fpr[1].d[0]; + } +#else + memcpy(dp, sp, ctr->size); /* Copy complex from GPRs. */ +#endif +#endif + return 1; /* Return cdata object which is already on top of stack. */ + } +#if CCALL_NUM_FPR + if (ctype_isfp(ctr->info) || ctype_isvector(ctr->info)) + sp = &cc->fpr[0]; +#endif + /* No reference types end up here, so there's no need for the CTypeID. */ + lua_assert(!(ctype_isrefarray(ctr->info) || ctype_isstruct(ctr->info))); + lj_cconv_tv_ct(cts, ctr, 0, L->top-1, (uint8_t *)sp); + return 1; /* One result. */ +} + +/* Call C function. */ +int lj_ccall_func(lua_State *L, GCcdata *cd) +{ + CTState *cts = ctype_cts(L); + CType *ct = ctype_raw(cts, cd->typeid); + CTSize sz = ct->size; + void *p = cdataptr(cd); + if (ctype_isptr(ct->info)) + ct = ctype_rawchild(cts, ct); + if (ctype_isfunc(ct->info)) { + CCallState cc; + cc.func = (void (*)(void))cdata_getptr(p, sz); + ccall_set_args(L, cts, ct, &cc); + lj_vm_ffi_call(&cc); + return ccall_get_results(L, cts, ct, &cc); + } + return -1; /* Not a function. */ +} + +#endif diff --git a/src/lj_ccall.h b/src/lj_ccall.h new file mode 100644 index 00000000..a0737d04 --- /dev/null +++ b/src/lj_ccall.h @@ -0,0 +1,131 @@ +/* +** FFI C call handling. +** Copyright (C) 2005-2010 Mike Pall. See Copyright Notice in luajit.h +*/ + +#ifndef _LJ_CCALL_H +#define _LJ_CCALL_H + +#include "lj_obj.h" + +#if LJ_HASFFI + +/* -- C calling conventions ----------------------------------------------- */ + +#if LJ_TARGET_X86ORX64 + +#if LJ_TARGET_X86 +#define CCALL_NARG_GPR 2 /* For fastcall arguments. */ +#define CCALL_NARG_FPR 0 +#define CCALL_NRET_GPR 2 +#define CCALL_NRET_FPR 1 /* For FP results on x87 stack. */ +#define CCALL_STRUCT_RETREF 1 /* Return structs by reference. */ +#define CCALL_COMPLEX_RETREF 1 /* Return complex by reference. */ +#define CCALL_COMPLEXF_RETREF 0 /* Return complex float by value. */ +#define CCALL_ALIGN_STACKARG 0 /* Don't align argument on stack. */ +#elif LJ_ABI_WIN +#define CCALL_NARG_GPR 4 +#define CCALL_NARG_FPR 4 +#define CCALL_NRET_GPR 1 +#define CCALL_NRET_FPR 1 +#define CCALL_SPS_EXTRA 4 +#define CCALL_COMPLEX_ARGREF 1 /* Pass complex by reference. */ +#define CCALL_COMPLEX_RETREF 1 /* Return complex by reference. */ +#define CCALL_COMPLEXF_RETREF 0 /* Return complex float by value. */ +#else +#define CCALL_NARG_GPR 6 +#define CCALL_NARG_FPR 8 +#define CCALL_NRET_GPR 2 +#define CCALL_NRET_FPR 2 +#define CCALL_VECTOR_REG 1 /* Pass vectors in registers. */ +#endif + +#define CCALL_SPS_FREE 1 + +typedef LJ_ALIGN(16) union FPRArg { + double d[2]; + float f[4]; + uint8_t b[16]; + uint16_t s[8]; + int i[4]; + int64_t l[2]; +} FPRArg; + +typedef intptr_t GPRArg; + +#elif LJ_TARGET_PPCSPE + +#define CCALL_NARG_GPR 8 +#define CCALL_NARG_FPR 0 +#define CCALL_NRET_GPR 4 /* For softfp complex double. */ +#define CCALL_NRET_FPR 0 +#define CCALL_SPS_FREE 0 /* NYI */ +#define CCALL_STRUCT_ARGREF 1 /* Pass structs by reference. */ +#define CCALL_STRUCT_RETREF 1 /* Return structs by reference. */ + +typedef intptr_t GPRArg; + +#else +#error "missing calling convention definitions for this architecture" +#endif + +#ifndef CCALL_SPS_EXTRA +#define CCALL_SPS_EXTRA 0 +#endif +#ifndef CCALL_STRUCT_ARGREF +#define CCALL_STRUCT_ARGREF 0 +#endif +#ifndef CCALL_STRUCT_RETREF +#define CCALL_STRUCT_RETREF 0 +#endif +#ifndef CCALL_COMPLEX_ARGREF +#define CCALL_COMPLEX_ARGREF 0 +#endif +#ifndef CCALL_COMPLEX_RETREF +#define CCALL_COMPLEX_RETREF 0 +#endif +#ifndef CCALL_COMPLEXF_RETREF +#define CCALL_COMPLEXF_RETREF CCALL_COMPLEX_RETREF +#endif +#ifndef CCALL_VECTOR_REG +#define CCALL_VECTOR_REG 0 +#endif +#ifndef CCALL_ALIGN_STACKARG +#define CCALL_ALIGN_STACKARG 1 +#endif + +#define CCALL_NUM_GPR \ + (CCALL_NARG_GPR > CCALL_NRET_GPR ? CCALL_NARG_GPR : CCALL_NRET_GPR) +#define CCALL_NUM_FPR \ + (CCALL_NARG_FPR > CCALL_NRET_FPR ? CCALL_NARG_FPR : CCALL_NRET_FPR) + +#define CCALL_MAXSTACK 32 + +/* -- C call state -------------------------------------------------------- */ + +typedef struct CCallState { + void (*func)(void); /* Pointer to called function. */ + uint32_t spadj; /* Stack pointer adjustment. */ + uint8_t nsp; /* Number of stack slots. */ + uint8_t retref; /* Return value by reference. */ +#if LJ_TARGET_X64 + uint8_t nfpr; /* Number of arguments in FPRs. */ +#elif LJ_TARGET_X86 + uint8_t resx87; /* Result on x87 stack: 1:float, 2:double. */ +#endif + GPRArg gpr[CCALL_NUM_GPR]; /* Arguments/results in GPRs. */ +#if CCALL_NUM_FPR + FPRArg fpr[CCALL_NUM_FPR]; /* Arguments/results in FPRs. */ +#endif + GPRArg stack[CCALL_MAXSTACK]; /* Stack slots. */ +} CCallState; + +/* -- C call handling ----------------------------------------------------- */ + +/* Really belongs to lj_vm.h. */ +LJ_ASMF void LJ_FASTCALL lj_vm_ffi_call(CCallState *cc); +LJ_FUNC int lj_ccall_func(lua_State *L, GCcdata *cd); + +#endif + +#endif diff --git a/src/lj_ctype.h b/src/lj_ctype.h index 826f9785..437f9ae8 100644 --- a/src/lj_ctype.h +++ b/src/lj_ctype.h @@ -35,6 +35,9 @@ enum { CT_KW /* Keyword. */ }; +LJ_STATIC_ASSERT(((int)CT_PTR & (int)CT_ARRAY) == CT_PTR); +LJ_STATIC_ASSERT(((int)CT_STRUCT & (int)CT_ARRAY) == CT_STRUCT); + /* ** ---------- info ------------ ** |type flags... A cid | size | sib | next | name | @@ -193,9 +196,8 @@ typedef struct CTState { (((info) & (CTMASK_NUM|CTF_FP)) == CTINFO(CT_NUM, 0)) #define ctype_isbool(info) \ (((info) & (CTMASK_NUM|CTF_BOOL)) == CTINFO(CT_NUM, CTF_BOOL)) -#define ctype_isconstchar(ct) \ - (((ct)->info & (CTMASK_NUM|CTF_CONST|CTF_BOOL|CTF_FP)) == \ - CTINFO(CT_NUM, CTF_CONST) && (ct)->size == 1) +#define ctype_isfp(info) \ + (((info) & (CTMASK_NUM|CTF_FP)) == CTINFO(CT_NUM, CTF_FP)) #define ctype_ispointer(info) \ ((ctype_type(info) >> 1) == (CT_PTR >> 1)) /* Pointer or array. */ @@ -204,8 +206,6 @@ typedef struct CTState { #define ctype_isrefarray(info) \ (((info) & (CTMASK_NUM|CTF_VECTOR|CTF_COMPLEX)) == CTINFO(CT_ARRAY, 0)) -#define ctype_isvalarray(info) \ - (ctype_isarray(info) && (info & (CTF_VECTOR|CTF_COMPLEX))) #define ctype_isvector(info) \ (((info) & (CTMASK_NUM|CTF_VECTOR)) == CTINFO(CT_ARRAY, CTF_VECTOR)) #define ctype_iscomplex(info) \ @@ -216,16 +216,11 @@ typedef struct CTState { CTINFO(CT_STRUCT, CTF_VLA)) /* VL array or VL struct. */ #define ctype_isvlarray(info) \ (((info) & (CTMASK_NUM|CTF_VLA)) == CTINFO(CT_ARRAY, CTF_VLA)) -#define ctype_isvlstruct(info) \ - (((info) & (CTMASK_NUM|CTF_VLA)) == CTINFO(CT_STRUCT, CTF_VLA)) #define ctype_isxattrib(info, at) \ (((info) & (CTMASK_NUM|CTATTRIB(CTMASK_ATTRIB))) == \ CTINFO(CT_ATTRIB, CTATTRIB(at))) -LJ_STATIC_ASSERT(((int)CT_PTR & (int)CT_ARRAY) == CT_PTR); -LJ_STATIC_ASSERT(((int)CT_STRUCT & (int)CT_ARRAY) == CT_STRUCT); - /* Target-dependent sizes and alignments. */ #if LJ_64 #define CTSIZE_PTR 8 diff --git a/src/lj_errmsg.h b/src/lj_errmsg.h index 66062747..ae784bc9 100644 --- a/src/lj_errmsg.h +++ b/src/lj_errmsg.h @@ -148,11 +148,13 @@ ERRDEF(FFI_REDEF, "attempt to redefine " LUA_QS) ERRDEF(FFI_INITOV, "too many initializers for " LUA_QS) ERRDEF(FFI_BADCONV, "cannot convert " LUA_QS " to " LUA_QS) ERRDEF(FFI_BADARITH, "attempt to perform arithmetic on " LUA_QS " and " LUA_QS) +ERRDEF(FFI_BADCALL, LUA_QS " is not callable") +ERRDEF(FFI_NUMARG, "wrong number of arguments for function call") ERRDEF(FFI_BADMEMBER, LUA_QS " has no member named " LUA_QS) ERRDEF(FFI_BADIDX, LUA_QS " cannot be indexed") ERRDEF(FFI_WRCONST, "attempt to write to constant location") ERRDEF(FFI_NYIPACKBIT, "NYI: packed bit fields") -ERRDEF(FFI_NYICALL, "NYI: C function calls") +ERRDEF(FFI_NYICALL, "NYI: cannot call this C function (yet)") #endif #undef ERRDEF diff --git a/src/ljamalg.c b/src/ljamalg.c index edcae247..56521752 100644 --- a/src/ljamalg.c +++ b/src/ljamalg.c @@ -43,6 +43,7 @@ #include "lj_ctype.c" #include "lj_cdata.c" #include "lj_cconv.c" +#include "lj_ccall.c" #include "lj_cparse.c" #include "lj_lib.c" #include "lj_ir.c"