[pypy-commit] pypy ffi-backend: Work in progress: building the CIF for libffi.
arigo
noreply at buildbot.pypy.org
Wed Jun 27 13:20:01 CEST 2012
Author: Armin Rigo <arigo at tunes.org>
Branch: ffi-backend
Changeset: r55858:ae148b8df011
Date: 2012-06-27 13:19 +0200
http://bitbucket.org/pypy/pypy/changeset/ae148b8df011/
Log: Work in progress: building the CIF for libffi.
diff --git a/pypy/module/_cffi_backend/ctypefunc.py b/pypy/module/_cffi_backend/ctypefunc.py
--- a/pypy/module/_cffi_backend/ctypefunc.py
+++ b/pypy/module/_cffi_backend/ctypefunc.py
@@ -2,8 +2,9 @@
Function pointers.
"""
-from pypy.rpython.lltypesystem import rffi
-from pypy.rlib import clibffi
+from pypy.interpreter.error import OperationError, operationerrfmt
+from pypy.rpython.lltypesystem import lltype, llmemory, rffi
+from pypy.rlib import jit, clibffi
from pypy.module._cffi_backend.ctypeptr import W_CTypePtrBase
@@ -11,6 +12,7 @@
class W_CTypeFunc(W_CTypePtrBase):
def __init__(self, space, fargs, fresult, ellipsis):
+ self.cif_descr = lltype.nullptr(CIF_DESCRIPTION)
extra = self._compute_extra_text(fargs, fresult, ellipsis)
size = rffi.sizeof(rffi.VOIDP)
W_CTypePtrBase.__init__(self, space, size, extra, 2, fresult,
@@ -24,7 +26,11 @@
# at all. The cif is computed on every call from the actual
# types passed in. For all other functions, the cif_descr
# is computed here.
- self.cif_descr = fb_prepare_cif(fargs, fresult)
+ CifDescrBuilder(fargs, fresult).rawallocate(self)
+
+ def __del__(self):
+ if self.cif_descr:
+ llmemory.raw_free(llmemory.cast_ptr_to_adr(self.cif_descr))
def _compute_extra_text(self, fargs, fresult, ellipsis):
argnames = ['(*)(']
@@ -50,19 +56,137 @@
# ____________________________________________________________
+# The "cif" is a block of raw memory describing how to do a call via libffi.
+# It starts with a block of memory of type FFI_CIF, which is used by libffi
+# itself. Following it, we find _cffi_backend-specific information:
+#
+# - 'exchange_size': an integer that tells how big a buffer we must
+# allocate for the call; this buffer should start with an array of
+# pointers to the actual argument values.
+#
+# - 'exchange_result': the offset in that buffer for the result of the call.
+#
+# - 'exchange_args[nargs]': the offset in that buffer for each argument.
+#
+# Following this, we have other data structures for libffi (with direct
+# pointers from the FFI_CIF to these data structures):
+#
+# - the argument types, as an array of 'ffi_type *'.
+#
+# - optionally, the result's and the arguments' ffi type data
+# (this is used only for 'struct' ffi types; in other cases the
+# 'ffi_type *' just points to static data like 'ffi_type_sint32').
+
+FFI_CIF = clibffi.FFI_CIFP.TO
+FFI_TYPE = clibffi.FFI_TYPE_P.TO
+FFI_TYPE_P = clibffi.FFI_TYPE_P
+FFI_TYPE_PP = clibffi.FFI_TYPE_PP
CIF_DESCRIPTION = lltype.Struct(
'CIF_DESCRIPTION',
- ('cif', clibffi.FFI_CIFP.TO),
- # the following information is used when doing the call:
- # - a buffer of size 'exchange_size' is malloced
- # - the arguments are converted from Python objects to raw data
- # - the i'th raw data is stored at 'buffer + exchange_offset_arg[1+i]'
- # - the call is done
- # - the result is read back from 'buffer + exchange_offset_arg[0]'
+ ('cif', FFI_CIF),
('exchange_size', lltype.Signed),
- ('exchange_offset_arg', lltype.Array(lltype.Signed)))
+ ('exchange_result', lltype.Signed),
+ ('exchange_args', lltype.Array(lltype.Signed)))
+CIF_DESCRIPTION_P = lltype.Ptr(CIF_DESCRIPTION)
-def fb_prepare_cif(fargs, fresult):
- ...
+
+class CifDescrBuilder(object):
+ rawmem = lltype.nullptr(rffi.CCHARP.TO)
+
+ def __init__(self, fargs, fresult):
+ self.fargs = fargs
+ self.fresult = fresult
+
+ def fb_alloc(self, size):
+ size = llmemory.raw_malloc_usage(size)
+ if not self.bufferp:
+ self.nb_bytes += size
+ return lltype.nullptr(rffi.CCHARP.TO)
+ else:
+ result = self.bufferp
+ self.bufferp = rffi.ptradd(result, size)
+ return result
+
+
+ def fb_fill_type(self, ctype):
+ xxx
+
+
+ def fb_build(self):
+ nargs = len(self.fargs)
+
+ # start with a cif_description (cif and exchange_* fields)
+ self.fb_alloc(llmemory.sizeof(CIF_DESCRIPTION, nargs))
+
+ # next comes an array of 'ffi_type*', one per argument
+ atypes = self.fb_alloc(rffi.sizeof(FFI_TYPE_P) * nargs)
+ self.atypes = rffi.cast(FFI_TYPE_PP, atypes)
+
+ # next comes the result type data
+ self.rtype = self.fb_fill_type(self.fresult)
+
+ # next comes each argument's type data
+ for farg in self.fargs:
+ atype = self.fb_fill_type(farg)
+ if self.atypes:
+ self.atypes[i] = atype
+
+
+ def align_arg(self, n):
+ return (n + 7) & ~7
+
+ def fb_build_exchange(self, cif_descr):
+ nargs = len(self.fargs)
+
+ # 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
+
+ # then enough room for the result --- which means at least
+ # sizeof(ffi_arg), according to the ffi docs (which is 8).
+ exchange_offset += max(self.rtype.c_size, 8)
+
+ # loop over args
+ for i, farg in enumerate(self.fargs):
+ exchange_offset = self.align_arg(exchange_offset)
+ cif_descr.exchange_args[i] = exchange_offset
+ exchange_offset += self.atypes[i].c_size
+
+ # store the exchange data size
+ cif_descr.exchange_size = exchange_offset
+
+
+ @jit.dont_look_inside
+ def rawallocate(self, ctypefunc):
+ # 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
+ rawmem = rffi.cast(rffi.CCHARP,
+ llmemory.raw_malloc(self.nb_bytes))
+
+ # the buffer is automatically managed from the W_CTypeFunc instance
+ ctypefunc.cif_descr = rffi.cast(CIF_DESCRIPTION_P, rawmem)
+
+ # call again fb_build() to really build the libffi data structures
+ self.bufferp = rawmem
+ self.fb_build()
+ assert self.bufferp == rawmem + self.nb_bytes
+
+ # fill in the 'exchange_*' fields
+ self.fb_build_exchange(ctypefunc.cif_descr)
+
+ # call libffi's ffi_prep_cif() function
+ cif = rffi.cast(FFI_CIFP, rawmem)
+ res = clibffi.c_ffi_prep_cif(cif, clibffi.FFI_DEFAULT_ABI,
+ len(self.fargs),
+ self.rtype, self.atypes)
+ if rffi.cast(lltype.Signed, res) != clibffi.FFI_OK:
+ space = ctypefunc.space
+ raise OperationError(space.w_SystemError,
+ space.wrap("libffi failed to build this function type"))
More information about the pypy-commit
mailing list