📄 ffi.c
字号:
} else { cif->flags = FFI_TYPE_STRUCT; } } break; case FFI_TYPE_FLOAT: is_simple = FALSE; cif->flags = FFI_TYPE_FLOAT; break; case FFI_TYPE_DOUBLE: is_simple = FALSE; cif->flags = FFI_TYPE_DOUBLE; break; default: cif->flags = FFI_TYPE_INT; /* This seems to depend on little endian mode, and the fact that */ /* the return pointer always points to at least 8 bytes. But */ /* that also seems to be true for other platforms. */ break; } if (is_simple) cif -> flags |= simple_flag; return FFI_OK;}extern int ffi_call_unix(bool (*)(struct ia64_args *, extended_cif *, int), extended_cif *, unsigned, unsigned, unsigned *, void (*)());voidffi_call(ffi_cif *cif, void (*fn)(), void *rvalue, void **avalue){ extended_cif ecif; long simple = cif -> flags & FFI_SIMPLE; /* Should this also check for Unix ABI? */ /* This is almost, but not quite, machine independent. Note that */ /* we can get away with not caring about length of the result because */ /* we assume we are little endian, and the result buffer is large */ /* enough. */ /* This needs work for HP/UX. */ if (simple) { long (*lfn)() = (long (*)())fn; long result; switch(simple) { case FFI_SIMPLE_V: result = lfn(); break; case FFI_SIMPLE_I: result = lfn(*(int *)avalue[0]); break; case FFI_SIMPLE_L: result = lfn(*(long *)avalue[0]); break; case FFI_SIMPLE_II: result = lfn(*(int *)avalue[0], *(int *)avalue[1]); break; case FFI_SIMPLE_IL: result = lfn(*(int *)avalue[0], *(long *)avalue[1]); break; case FFI_SIMPLE_LI: result = lfn(*(long *)avalue[0], *(int *)avalue[1]); break; case FFI_SIMPLE_LL: result = lfn(*(long *)avalue[0], *(long *)avalue[1]); break; } if ((cif->flags & ~FFI_SIMPLE) != FFI_TYPE_VOID && 0 != rvalue) { * (long *)rvalue = result; } return; } ecif.cif = cif; ecif.avalue = avalue; /* If the return value is a struct and we don't have a return value address then we need to make one. */ if (rvalue == NULL && cif->rtype->type == FFI_TYPE_STRUCT) ecif.rvalue = alloca(cif->rtype->size); else ecif.rvalue = rvalue; switch (cif->abi) { case FFI_UNIX: ffi_call_unix(ffi_prep_args, &ecif, cif->bytes, cif->flags, rvalue, fn); break; default: FFI_ASSERT(0); break; }}/* * Closures represent a pair consisting of a function pointer, and * some user data. A closure is invoked by reinterpreting the closure * as a function pointer, and branching to it. Thus we can make an * interpreted function callable as a C function: We turn the interpreter * itself, together with a pointer specifying the interpreted procedure, * into a closure. * On X86, the first few words of the closure structure actually contain code, * which will do the right thing. On most other architectures, this * would raise some Icache/Dcache coherence issues (which can be solved, but * often not cheaply). * For IA64, function pointer are already pairs consisting of a code * pointer, and a gp pointer. The latter is needed to access global variables. * Here we set up such a pair as the first two words of the closure (in * the "trampoline" area), but we replace the gp pointer with a pointer * to the closure itself. We also add the real gp pointer to the * closure. This allows the function entry code to both retrieve the * user data, and to restire the correct gp pointer. */static void ffi_prep_incoming_args_UNIX(struct ia64_args *args, void **rvalue, void **avalue, ffi_cif *cif);/* This function is entered with the doctored gp (r1) value. * This code is extremely gcc specific. There is some argument that * it should really be written in assembly code, since it depends on * gcc properties that might change over time. *//* ffi_closure_UNIX is an assembly routine, which copies the register *//* state into a struct ia64_args, and then invokes *//* ffi_closure_UNIX_inner. It also recovers the closure pointer *//* from its fake gp pointer. */void ffi_closure_UNIX();#ifndef __GNUC__# error This requires gcc#endifvoidffi_closure_UNIX_inner (ffi_closure *closure, struct ia64_args * args)/* Hopefully declaring this as a varargs function will force all args *//* to memory. */{ // this is our return value storage long double res; // our various things... ffi_cif *cif; unsigned short rtype; void *resp; void **arg_area; resp = (void*)&res; cif = closure->cif; arg_area = (void**) alloca (cif->nargs * sizeof (void*)); /* this call will initialize ARG_AREA, such that each * element in that array points to the corresponding * value on the stack; and if the function returns * a structure, it will re-set RESP to point to the * structure return address. */ ffi_prep_incoming_args_UNIX(args, (void**)&resp, arg_area, cif); (closure->fun) (cif, resp, arg_area, closure->user_data); rtype = cif->flags; /* now, do a generic return based on the value of rtype */ if (rtype == FFI_TYPE_INT) { asm volatile ("ld8 r8=[%0]" : : "r" (resp) : "r8"); } else if (rtype == FFI_TYPE_FLOAT) { asm volatile ("ldfs f8=[%0]" : : "r" (resp) : "f8"); } else if (rtype == FFI_TYPE_DOUBLE) { asm volatile ("ldfd f8=[%0]" : : "r" (resp) : "f8"); } else if (rtype == FFI_IS_SMALL_STRUCT2) { asm volatile ("ld8 r8=[%0]; ld8 r9=[%1]" : : "r" (resp), "r" (resp+8) : "r8","r9"); } else if (rtype == FFI_IS_SMALL_STRUCT3) { asm volatile ("ld8 r8=[%0]; ld8 r9=[%1]; ld8 r10=[%2]" : : "r" (resp), "r" (resp+8), "r" (resp+16) : "r8","r9","r10"); } else if (rtype == FFI_IS_SMALL_STRUCT4) { asm volatile ("ld8 r8=[%0]; ld8 r9=[%1]; ld8 r10=[%2]; ld8 r11=[%3]" : : "r" (resp), "r" (resp+8), "r" (resp+16), "r" (resp+24) : "r8","r9","r10","r11"); } else if (rtype != FFI_TYPE_VOID && rtype != FFI_TYPE_STRUCT) { /* Can only happen for homogeneous FP aggregates? */ abort(); }}static void ffi_prep_incoming_args_UNIX(struct ia64_args *args, void **rvalue, void **avalue, ffi_cif *cif){ register unsigned int i; register unsigned int avn; register void **p_argv; register unsigned long *argp = args -> out_regs; unsigned fp_reg_num = 0; register ffi_type **p_arg; avn = cif->nargs; p_argv = avalue; for (i = cif->nargs, p_arg = cif->arg_types; i != 0; i--, p_arg++) { size_t z; /* In units of words or argument slots. */ switch ((*p_arg)->type) { case FFI_TYPE_SINT8: case FFI_TYPE_UINT8: case FFI_TYPE_SINT16: case FFI_TYPE_UINT16: case FFI_TYPE_SINT32: case FFI_TYPE_UINT32: case FFI_TYPE_SINT64: case FFI_TYPE_UINT64: case FFI_TYPE_POINTER: z = 1; *p_argv = (void *)argp; break; case FFI_TYPE_FLOAT: z = 1; /* Convert argument back to float in place from the saved value */ if (fp_reg_num < 8) { *(float *)argp = args -> fp_regs[fp_reg_num++]; } else { *(float *)argp = *(double *)argp; } *p_argv = (void *)argp; break; case FFI_TYPE_DOUBLE: z = 1; if (fp_reg_num < 8) { *p_argv = args -> fp_regs + fp_reg_num++; } else { *p_argv = (void *)argp; } break; case FFI_TYPE_STRUCT: { size_t sz = (*p_arg)->size; unsigned short element_type; z = ((*p_arg)->size + SIZEOF_ARG - 1)/SIZEOF_ARG; if (is_homogeneous_fp_aggregate(*p_arg, 8, &element_type)) { int nelements = sz/float_type_size(element_type); if (nelements + fp_reg_num >= 8) { /* hard case NYI. */ abort(); } if (element_type == FFI_TYPE_DOUBLE) { *p_argv = args -> fp_regs + fp_reg_num; fp_reg_num += nelements; break; } if (element_type == FFI_TYPE_FLOAT) { int j; for (j = 0; j < nelements; ++ j) { ((float *)argp)[j] = args -> fp_regs[fp_reg_num + j]; } *p_argv = (void *)argp; fp_reg_num += nelements; break; } abort(); /* Other fp types NYI */ } } break; default: FFI_ASSERT(0); } argp += z; p_argv++; } return;}/* Fill in a closure to refer to the specified fun and user_data. *//* cif specifies the argument and result types for fun. *//* the cif must already be prep'ed *//* The layout of a function descriptor. A C function pointer really *//* points to one of these. */typedef struct ia64_fd_struct { void *code_pointer; void *gp;} ia64_fd;ffi_statusffi_prep_closure (ffi_closure* closure, ffi_cif* cif, void (*fun)(ffi_cif*,void*,void**,void*), void *user_data){ struct ffi_ia64_trampoline_struct *tramp = (struct ffi_ia64_trampoline_struct *) (closure -> tramp); ia64_fd *fd = (ia64_fd *)(void *)ffi_closure_UNIX; FFI_ASSERT (cif->abi == FFI_UNIX); tramp -> code_pointer = fd -> code_pointer; tramp -> real_gp = fd -> gp; tramp -> fake_gp = closure; closure->cif = cif; closure->user_data = user_data; closure->fun = fun; return FFI_OK;}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -