def test_jit_ffi_call(): cd = lltype.malloc(CIF_DESCRIPTION, 1, flavor='raw') cd.abi = clibffi.FFI_DEFAULT_ABI cd.nargs = 1 cd.rtype = clibffi.cast_type_to_ffitype(rffi.DOUBLE) atypes = lltype.malloc(clibffi.FFI_TYPE_PP.TO, 1, flavor='raw') atypes[0] = clibffi.cast_type_to_ffitype(rffi.DOUBLE) cd.atypes = atypes cd.exchange_size = 64 # 64 bytes of exchange data cd.exchange_result = 24 cd.exchange_args[0] = 16 # jit_ffi_prep_cif(cd) # assert rffi.sizeof(rffi.DOUBLE) == 8 exb = lltype.malloc(rffi.DOUBLEP.TO, 8, flavor='raw') exb[2] = 1.23 jit_ffi_call(cd, math_sin, rffi.cast(rffi.CCHARP, exb)) res = exb[3] lltype.free(exb, flavor='raw') # lltype.free(atypes, flavor='raw') lltype.free(cd, flavor='raw') # assert res == math.sin(1.23)
def f(): # jit_ffi_prep_cif(cd) # assert rffi.sizeof(rffi.DOUBLE) == 8 exb = lltype.malloc(rffi.DOUBLEP.TO, 8, flavor='raw') exb[2] = 1.23 jit_ffi_call(cd, math_sin, rffi.cast(rffi.CCHARP, exb)) res = exb[3] lltype.free(exb, flavor='raw') # return res
def prepare_cif(self): argc = len(self.argtypes) # atypes points to an array of ffi_type pointers cif = lltype.malloc(jit_libffi.CIF_DESCRIPTION, argc, flavor='raw') cif.abi = clibffi.FFI_DEFAULT_ABI cif.atypes = lltype.malloc(clibffi.FFI_TYPE_PP.TO, argc, flavor='raw') for i in range(argc): cif.atypes[i] = self.argtypes[i].cast_to_ffitype() cif.nargs = argc restype = self.restype if restype is null: cif.rtype = clibffi.ffi_type_void # MSVC returns small structures in registers. Pretend int32 or # int64 return type. This is needed as a workaround for what # is really a bug of libffi_msvc seen as an independent library # (ctypes and pypy has a similar workaround). # Implementation of this workaround doesn't reach to elsewhere from here. elif USE_C_LIBFFI_MSVC and isinstance(restype, Struct) and restype.size <= 4: cif.rtype = clibffi.ffi_type_sint32 elif USE_C_LIBFFI_MSVC and isinstance(restype, Struct) and restype.size <= 8: cif.rtype = clibffi.ffi_type_sint64 else: cif.rtype = restype.cast_to_ffitype() exchange_size = argc * rffi.sizeof(rffi.VOIDPP) exchange_size = align(exchange_size, 8) for i in range(argc): argtype = self.argtypes[i] assert isinstance(argtype, Type) # checked prior that this holds. exchange_size = align(exchange_size, max(8, argtype.align)) cif.exchange_args[i] = int(exchange_size) exchange_size += sizeof(argtype) #cif.exchange_result_libffi = exchange_size if restype is null: exchange_size = align(exchange_size, 8) cif.exchange_result = int(exchange_size) exchange_size += jit_libffi.SIZE_OF_FFI_ARG elif isinstance(restype, Type): exchange_size = align(exchange_size, max(8, restype.align)) cif.exchange_result = int(exchange_size) exchange_size += max(sizeof(restype), jit_libffi.SIZE_OF_FFI_ARG) else: # SIZE_OF_FFI_ARG assert False # checked prior that this holds. cif.exchange_size = int(align(exchange_size, 8)) jit_libffi.jit_ffi_prep_cif(cif) self.cif = cif
def rawallocate(self, ctypefunc): space = ctypefunc.space self.space = space # compute the total size needed in the CIF_DESCRIPTION buffer self.nb_bytes = 0 self.bufferp = lltype.nullptr(rffi.CCHARP.TO) self.fb_build() # allocate the buffer if we_are_translated(): rawmem = lltype.malloc(rffi.CCHARP.TO, self.nb_bytes, flavor="raw") rawmem = rffi.cast(CIF_DESCRIPTION_P, rawmem) else: # gross overestimation of the length below, but too bad rawmem = lltype.malloc(CIF_DESCRIPTION_P.TO, self.nb_bytes, flavor="raw") # the buffer is automatically managed from the W_CTypeFunc instance ctypefunc.cif_descr = rawmem # call again fb_build() to really build the libffi data structures self.bufferp = rffi.cast(rffi.CCHARP, rawmem) self.fb_build() assert self.bufferp == rffi.ptradd(rffi.cast(rffi.CCHARP, rawmem), self.nb_bytes) # fill in the 'exchange_*' fields self.fb_build_exchange(rawmem) # fill in the extra fields self.fb_extra_fields(rawmem) # call libffi's ffi_prep_cif() function res = jit_libffi.jit_ffi_prep_cif(rawmem) if res != clibffi.FFI_OK: raise OperationError(space.w_SystemError, space.wrap("libffi failed to build this function type"))
def thaw(self): if not self._is_inited: self._f_ptr = self._lib.get_fn_ptr(self._name) nargs = len(self._arg_types) exchange_buffer_size = nargs * rffi.sizeof(rffi.CCHARP) cd = lltype.malloc(CIF_DESCRIPTION, nargs, flavor="raw") cd.abi = clibffi.FFI_DEFAULT_ABI cd.nargs = nargs cd.rtype = self._ret_type.ffi_type() atypes = lltype.malloc(clibffi.FFI_TYPE_PP.TO, nargs, flavor="raw") arg0_offset = exchange_buffer_size for idx in range(nargs): cd.exchange_args[idx] = exchange_buffer_size tp = self._arg_types[idx] native_size = tp.ffi_size() atypes[idx] = tp.ffi_type() exchange_buffer_size += native_size ret_offset = exchange_buffer_size exchange_buffer_size += self._ret_type.ffi_size() cd.atypes = atypes cd.exchange_size = exchange_buffer_size cd.exchange_result = ret_offset cd.exchange_result_libffi = ret_offset jit_ffi_prep_cif(cd) self._cd = cd self._transfer_size = exchange_buffer_size self._arg0_offset = arg0_offset self._ret_offset = ret_offset self._is_inited = True return self
def thaw(self): if not self._is_inited: self._f_ptr = self._lib.get_fn_ptr(self._name) transfer_size = 0 arg0_offset = len(self._arg_types) * rffi.sizeof(rffi.CCHARP) exchange_result = arg0_offset for x in self._arg_types: exchange_result = transfer_size transfer_size += get_native_size(x) ret_offset = transfer_size transfer_size += get_native_size(self._ret_type) cd = lltype.malloc(CIF_DESCRIPTION, len(self._arg_types), flavor="raw") cd.abi = clibffi.FFI_DEFAULT_ABI cd.nargs = len(self._arg_types) cd.rtype = get_clibffi_type(self._ret_type) atypes = lltype.malloc(clibffi.FFI_TYPE_PP.TO, len(self._arg_types), flavor="raw") for x in range(len(self._arg_types)): atypes[x] = get_clibffi_type(self._arg_types[x]) cd.atypes = atypes cd.exchange_size = transfer_size cd.exchange_result = ret_offset cd.exchange_result_libffi = ret_offset cd.exchange_args[0] = arg0_offset jit_ffi_prep_cif(cd) self._cd = cd self._transfer_size = transfer_size self._arg0_offset = arg0_offset self._ret_offset = ret_offset self._is_inited = True return self
def prepare_cif(self): argc = len(self.argtypes) # atypes points to an array of ffi_type pointers cif = lltype.malloc(jit_libffi.CIF_DESCRIPTION, argc, flavor='raw') cif.abi = clibffi.FFI_DEFAULT_ABI cif.atypes = lltype.malloc(clibffi.FFI_TYPE_PP.TO, argc, flavor='raw') for i in range(argc): cif.atypes[i] = self.argtypes[i].cast_to_ffitype() cif.nargs = argc if self.restype is null: cif.rtype = clibffi.ffi_type_void else: cif.rtype = self.restype.cast_to_ffitype() exchange_size = argc * rffi.sizeof(rffi.VOIDPP) exchange_size = align(exchange_size, 8) for i in range(argc): argtype = self.argtypes[i] assert isinstance(argtype, Type) # checked prior that this holds. exchange_size = align(exchange_size, max(8, argtype.align)) cif.exchange_args[i] = int(exchange_size) exchange_size += sizeof(argtype) #cif.exchange_result_libffi = exchange_size restype = self.restype if restype is null: exchange_size = align(exchange_size, 8) cif.exchange_result = int(exchange_size) exchange_size += jit_libffi.SIZE_OF_FFI_ARG elif isinstance(restype, Type): exchange_size = align(exchange_size, max(8, restype.align)) cif.exchange_result = int(exchange_size) exchange_size += max(sizeof(restype), jit_libffi.SIZE_OF_FFI_ARG) else: # SIZE_OF_FFI_ARG assert False # checked prior that this holds. cif.exchange_size = int(align(exchange_size, 8)) jit_libffi.jit_ffi_prep_cif(cif) self.cif = cif
def prepare_cif(self): argc = len(self.argtypes) # atypes points to an array of ffi_type pointers cif = lltype.malloc(jit_libffi.CIF_DESCRIPTION, argc, flavor='raw') cif.abi = clibffi.FFI_DEFAULT_ABI cif.atypes = lltype.malloc(clibffi.FFI_TYPE_PP.TO, argc, flavor='raw') for i in range(argc): cif.atypes[i] = self.argtypes[i].cast_to_ffitype() cif.nargs = argc if self.restype is null: cif.rtype = clibffi.ffi_type_void else: cif.rtype = self.restype.cast_to_ffitype() exchange_size = argc * rffi.sizeof(rffi.VOIDPP) for i in range(argc): argtype = self.argtypes[i] assert isinstance(argtype, Type) exchange_size = align(exchange_size, argtype.align) cif.exchange_args[i] = exchange_size exchange_size += sizeof(argtype) cif.exchange_result = exchange_size #cif.exchange_result_libffi = exchange_size restype = self.restype if restype is null: exchange_size += 0 elif isinstance(restype, Type): exchange_size = align(exchange_size, restype.align) exchange_size += sizeof(restype) else: assert False cif.exchange_size = align(exchange_size, 8) jit_libffi.jit_ffi_prep_cif(cif) self.cif = cif
def rawallocate(self, ctypefunc): space = ctypefunc.space self.space = space # compute the total size needed in the CIF_DESCRIPTION buffer self.nb_bytes = 0 self.bufferp = lltype.nullptr(rffi.CCHARP.TO) self.fb_build() # allocate the buffer if we_are_translated(): rawmem = lltype.malloc(rffi.CCHARP.TO, self.nb_bytes, flavor='raw') rawmem = rffi.cast(CIF_DESCRIPTION_P, rawmem) else: # gross overestimation of the length below, but too bad rawmem = lltype.malloc(CIF_DESCRIPTION_P.TO, self.nb_bytes, flavor='raw') # the buffer is automatically managed from the W_CTypeFunc instance ctypefunc.cif_descr = rawmem # call again fb_build() to really build the libffi data structures self.bufferp = rffi.cast(rffi.CCHARP, rawmem) self.fb_build() assert self.bufferp == rffi.ptradd(rffi.cast(rffi.CCHARP, rawmem), self.nb_bytes) # fill in the 'exchange_*' fields self.fb_build_exchange(rawmem) # fill in the extra fields self.fb_extra_fields(rawmem) # call libffi's ffi_prep_cif() function res = jit_libffi.jit_ffi_prep_cif(rawmem) if res != clibffi.FFI_OK: raise OperationError( space.w_SystemError, space.wrap("libffi failed to build this function type"))
def builtin_ffi_function(ctx): # parameter validation assert len(ctx.params) == 4 and \ ctx.params[0].type == 'str' and \ ctx.params[1].type == 'str' \ and ctx.params[2].type == 'str' \ and ctx.params[3].type == 'array' for e in ctx.params[3].arrayvalue: assert e.type == 'str' # extract parameters libname = ctx.params[0].strvalue funcname = ctx.params[1].strvalue rtype = ctx.params[2].strvalue atypes = [e.strvalue for e in ctx.params[3].arrayvalue] # validate if we defined before if (libname, funcname) in ctx.machine.space.ffi_functions: ctx.machine.error = ctx.machine.space.newstr( 'cannot define %s twice in %s.' % (funcname, libname)) return # setup cif argc = len(atypes) cif = lltype.malloc(jit_libffi.CIF_DESCRIPTION, argc, flavor='raw') cif.abi = clibffi.FFI_DEFAULT_ABI cif.nargs = argc cif.rtype = _cast_aotype_to_ffitype(rtype) cif.atypes = lltype.malloc(clibffi.FFI_TYPE_PP.TO, argc, flavor='raw') # create room for an array of nargs pointers exchange_offset = rffi.sizeof(rffi.VOIDP) * argc exchange_offset = _align(exchange_offset) cif.exchange_result = exchange_offset # create room for return value, roundup to sizeof(ffi-arg) exchange_offset += max(rffi.getintfield(cif.rtype, 'c_size'), jit_libffi.SIZE_OF_FFI_ARG) # set size for each arg for i in range(argc): atype = _cast_aotype_to_ffitype(atypes[i]) cif.atypes[i] = atype exchange_offset = _align(exchange_offset) cif.exchange_args[i] = exchange_offset exchange_offset += rffi.getintfield(atype, 'c_size') # set total size of args + retval cif.exchange_size = exchange_offset # prepare cif code = jit_libffi.jit_ffi_prep_cif(cif) if code != clibffi.FFI_OK: ctx.machine.error = ctx.machine.space.newstr( 'failed to build function %s for lib %s.' % (funcname, libname)) return # cache ffi ffi = ctx.machine.space.newforeignfunction(libname, funcname, rtype, atypes, cif) ctx.machine.space.ffi_functions[(libname, funcname)] = ffi ctx.tos.push(ctx.machine.space.null)
def build_cif_descr(self, space): arg_types_w = self.arg_types_w w_ret_type = self.w_ret_type assert isinstance(w_ret_type, W_TypeObject) ffi_arg_types = [] for w_arg_type in arg_types_w: assert isinstance(w_arg_type, W_TypeObject) ffi_arg_type = ffitype.ffi_types[w_arg_type.typeindex] ffi_arg_types.append(ffi_arg_type) ffi_ret_type = ffitype.ffi_types[w_ret_type.typeindex] nargs = len(ffi_arg_types) # XXX combine both mallocs with alignment size = llmemory.raw_malloc_usage( llmemory.sizeof(CIF_DESCRIPTION, nargs)) if we_are_translated(): cif_descr = lltype.malloc(rffi.CCHARP.TO, size, flavor='raw') cif_descr = rffi.cast(CIF_DESCRIPTION_P, cif_descr) else: # gross overestimation of the length below, but too bad cif_descr = lltype.malloc(CIF_DESCRIPTION_P.TO, size, flavor='raw') assert cif_descr # size = rffi.sizeof(FFI_TYPE_P) * nargs atypes = lltype.malloc(rffi.CCHARP.TO, size, flavor='raw') atypes = rffi.cast(FFI_TYPE_PP, atypes) assert atypes # cif_descr.abi = clibffi.FFI_DEFAULT_ABI cif_descr.nargs = nargs cif_descr.rtype = ffi_ret_type cif_descr.atypes = atypes # # first, enough room for an array of 'nargs' pointers exchange_offset = rffi.sizeof(rffi.CCHARP) * nargs exchange_offset = self.align_arg(exchange_offset) cif_descr.exchange_result = exchange_offset # cif_descr.exchange_result_libffi = exchange_offset # if BIG_ENDIAN: assert 0, 'missing support' # see _cffi_backend in pypy # then enough room for the result, rounded up to sizeof(ffi_arg) exchange_offset += max(rffi.getintfield(ffi_ret_type, 'c_size'), SIZE_OF_FFI_ARG) # loop over args for i, ffi_arg in enumerate(ffi_arg_types): # XXX do we need the "must free" logic? exchange_offset = self.align_arg(exchange_offset) cif_descr.exchange_args[i] = exchange_offset atypes[i] = ffi_arg exchange_offset += rffi.getintfield(ffi_arg, 'c_size') # store the exchange data size cif_descr.exchange_size = exchange_offset # status = jit_ffi_prep_cif(cif_descr) # if status != clibffi.FFI_OK: raise space.error(space.w_RuntimeError, "libffi failed to build this function type") # return cif_descr
def _setup(self, cppthis): self.converters = [converter.get_converter(self.space, arg_type, arg_dflt) for arg_type, arg_dflt in self.arg_defs] self.executor = executor.get_executor( self.space, capi.c_method_result_type(self.space, self.scope, self.index)) for conv in self.converters: if conv.uses_local: self.uses_local = True break # Each CPPMethod corresponds one-to-one to a C++ equivalent and cppthis # has been offset to the matching class. Hence, the libffi pointer is # uniquely defined and needs to be setup only once. methgetter = capi.c_get_methptr_getter(self.space, self.scope, self.index) if methgetter and cppthis: # methods only for now cif_descr = lltype.nullptr(jit_libffi.CIF_DESCRIPTION) try: funcaddr = methgetter(rffi.cast(capi.C_OBJECT, cppthis)) self._funcaddr = rffi.cast(rffi.VOIDP, funcaddr) nargs = self.args_expected + 1 # +1: cppthis # memory block for CIF description (note: not tracked as the life # time of methods is normally the duration of the application) size = llmemory.sizeof(jit_libffi.CIF_DESCRIPTION, nargs) # allocate the buffer cif_descr = lltype.malloc(jit_libffi.CIF_DESCRIPTION_P.TO, llmemory.raw_malloc_usage(size), flavor='raw', track_allocation=False) # array of 'ffi_type*' values, one per argument size = rffi.sizeof(jit_libffi.FFI_TYPE_P) * nargs atypes = lltype.malloc(rffi.CCHARP.TO, llmemory.raw_malloc_usage(size), flavor='raw', track_allocation=False) cif_descr.atypes = rffi.cast(jit_libffi.FFI_TYPE_PP, atypes) # argument type specification cif_descr.atypes[0] = jit_libffi.types.pointer # cppthis for i, conv in enumerate(self.converters): if not conv.libffitype: raise FastCallNotPossible cif_descr.atypes[i+1] = conv.libffitype # result type specification cif_descr.rtype = self.executor.libffitype # exchange --- # first, enough room for an array of 'nargs' pointers exchange_offset = rffi.sizeof(rffi.CCHARP) * nargs exchange_offset = (exchange_offset + 7) & ~7 # alignment cif_descr.exchange_result = exchange_offset cif_descr.exchange_result_libffi = exchange_offset # TODO: left this out while testing (see ctypefunc.py) # For results of precisely these types, libffi has a # strange rule that they will be returned as a whole # 'ffi_arg' if they are smaller. The difference # only matters on big-endian. # then enough room for the result, rounded up to sizeof(ffi_arg) exchange_offset += max(rffi.getintfield(cif_descr.rtype, 'c_size'), jit_libffi.SIZE_OF_FFI_ARG) # loop over args for i in range(nargs): exchange_offset = (exchange_offset + 7) & ~7 # alignment cif_descr.exchange_args[i] = exchange_offset exchange_offset += rffi.getintfield(cif_descr.atypes[i], 'c_size') # store the exchange data size cif_descr.exchange_size = exchange_offset # --- exchange # extra cif_descr.abi = clibffi.FFI_DEFAULT_ABI cif_descr.nargs = self.args_expected + 1 # +1: cppthis res = jit_libffi.jit_ffi_prep_cif(cif_descr) if res != clibffi.FFI_OK: raise FastCallNotPossible except Exception, e: if cif_descr: lltype.free(cif_descr.atypes, flavor='raw', track_allocation=False) lltype.free(cif_descr, flavor='raw', track_allocation=False) cif_descr = lltype.nullptr(jit_libffi.CIF_DESCRIPTION) self._funcaddr = lltype.nullptr(rffi.VOIDP.TO) self.cif_descr = cif_descr
def build_cif_descr(self, space): arg_types_w = self.arg_types_w w_ret_type = self.w_ret_type assert isinstance(w_ret_type, W_TypeObject) ffi_arg_types = [] for w_arg_type in arg_types_w: assert isinstance(w_arg_type, W_TypeObject) ffi_arg_type = ffitype.ffi_types[w_arg_type.typeindex] ffi_arg_types.append(ffi_arg_type) ffi_ret_type = ffitype.ffi_types[w_ret_type.typeindex] nargs = len(ffi_arg_types) # XXX combine both mallocs with alignment size = llmemory.raw_malloc_usage( llmemory.sizeof(CIF_DESCRIPTION, nargs)) if we_are_translated(): cif_descr = lltype.malloc(rffi.CCHARP.TO, size, flavor='raw') cif_descr = rffi.cast(CIF_DESCRIPTION_P, cif_descr) else: # gross overestimation of the length below, but too bad cif_descr = lltype.malloc(CIF_DESCRIPTION_P.TO, size, flavor='raw') assert cif_descr # size = rffi.sizeof(FFI_TYPE_P) * nargs atypes = lltype.malloc(rffi.CCHARP.TO, size, flavor='raw') atypes = rffi.cast(FFI_TYPE_PP, atypes) assert atypes # cif_descr.abi = clibffi.FFI_DEFAULT_ABI cif_descr.nargs = nargs cif_descr.rtype = ffi_ret_type cif_descr.atypes = atypes # # first, enough room for an array of 'nargs' pointers exchange_offset = rffi.sizeof(rffi.CCHARP) * nargs exchange_offset = self.align_arg(exchange_offset) cif_descr.exchange_result = exchange_offset # cif_descr.exchange_result_libffi = exchange_offset # if BIG_ENDIAN: assert 0, 'missing support' # see _cffi_backend in pypy # then enough room for the result, rounded up to sizeof(ffi_arg) exchange_offset += max(rffi.getintfield(ffi_ret_type, 'c_size'), SIZE_OF_FFI_ARG) # loop over args for i, ffi_arg in enumerate(ffi_arg_types): # XXX do we need the "must free" logic? exchange_offset = self.align_arg(exchange_offset) cif_descr.exchange_args[i] = exchange_offset atypes[i] = ffi_arg exchange_offset += rffi.getintfield(ffi_arg, 'c_size') # store the exchange data size cif_descr.exchange_size = exchange_offset # status = jit_ffi_prep_cif(cif_descr) # if status != clibffi.FFI_OK: raise space.error( space.w_RuntimeError, "libffi failed to build this function type") # return cif_descr