[pypy-svn] r79521 - in pypy/branch/reflex-support/pypy: module/cppyy rlib

cfbolz at codespeak.net cfbolz at codespeak.net
Thu Nov 25 17:23:26 CET 2010


Author: cfbolz
Date: Thu Nov 25 17:23:22 2010
New Revision: 79521

Modified:
   pypy/branch/reflex-support/pypy/module/cppyy/converter.py
   pypy/branch/reflex-support/pypy/module/cppyy/executor.py
   pypy/branch/reflex-support/pypy/module/cppyy/interp_cppyy.py
   pypy/branch/reflex-support/pypy/rlib/libffi.py
Log:
Support for much faster calls in cppyy: use rlib.libffi to do the calls, which
should enable the JIT to optimize them into direct calls without any allocation.
This is so far restricted to some argument/return types, but can be extended.


Modified: pypy/branch/reflex-support/pypy/module/cppyy/converter.py
==============================================================================
--- pypy/branch/reflex-support/pypy/module/cppyy/converter.py	(original)
+++ pypy/branch/reflex-support/pypy/module/cppyy/converter.py	Thu Nov 25 17:23:22 2010
@@ -3,7 +3,7 @@
 from pypy.interpreter.buffer import Buffer
 from pypy.rpython.lltypesystem import rffi, lltype
 from pypy.rlib.rarithmetic import r_singlefloat
-from pypy.rlib import jit
+from pypy.rlib import jit, libffi
 
 from pypy.module._rawffi.interp_rawffi import unpack_simple_shape
 from pypy.module._rawffi.array import W_Array
@@ -20,6 +20,8 @@
 
 
 class TypeConverter(object):
+    libffitype = libffi.types.NULL
+
     def __init__(self, space, array_size):
         pass
 
@@ -35,6 +37,10 @@
     def convert_argument(self, space, w_obj):
         self._is_abstract()
 
+    def convert_argument_libffi(self, space, w_obj, argchain):
+        from pypy.module.cppyy.interp_cppyy import FastCallNotPossible
+        raise FastCallNotPossible
+
     def from_memory(self, space, w_obj, offset):
         self._is_abstract()
 
@@ -55,6 +61,8 @@
 
 
 class VoidConverter(TypeConverter):
+    libffitype = libffi.types.void
+
     def __init__(self, space, name):
         self.name = name
 
@@ -91,6 +99,8 @@
            fieldptr[0] = '\x00'
 
 class CharConverter(TypeConverter):
+    libffitype = libffi.types.schar
+
     def _from_space(self, space, w_value):
         # allow int to pass to char and make sure that str is of length 1
         if space.isinstance_w(w_value, space.w_int):
@@ -122,12 +132,20 @@
         fieldptr[0] = self._from_space(space, w_value)
 
 class LongConverter(TypeConverter):
+    libffitype = libffi.types.slong
+
+    def _unwrap_object(self, space, w_obj):
+        return space.c_int_w(w_obj)
+
     def convert_argument(self, space, w_obj):
-        arg = space.c_int_w(w_obj)
+        arg = self._unwrap_object(space, w_obj)
         x = lltype.malloc(rffi.LONGP.TO, 1, flavor='raw')
         x[0] = arg
         return rffi.cast(rffi.VOIDP, x)
 
+    def convert_argument_libffi(self, space, w_obj, argchain):
+        argchain.arg(self._unwrap_object(space, w_obj))
+
     def from_memory(self, space, w_obj, offset):
         fieldptr = self._get_fieldptr(space, w_obj, offset)
         longptr = rffi.cast(rffi.LONGP, fieldptr)
@@ -139,6 +157,8 @@
         longptr[0] = space.c_int_w(w_value)
 
 class ShortConverter(LongConverter):
+    libffitype = libffi.types.sshort
+
     def from_memory(self, space, w_obj, offset):
         fieldptr = self._get_fieldptr(space, w_obj, offset)
         shortptr = rffi.cast(rffi.SHORTP, fieldptr)
@@ -150,11 +170,13 @@
         shortptr[0] = rffi.cast(rffi.SHORT, space.c_int_w(w_value))
 
 class FloatConverter(TypeConverter):
+    def _unwrap_object(self, space, w_obj):
+        return r_singlefloat(space.float_w(w_obj))
+
     def convert_argument(self, space, w_obj):
-        arg = space.float_w(w_obj)
         x = lltype.malloc(rffi.FLOATP.TO, 1, flavor='raw')
-        x[0] = r_singlefloat(arg)
-        return rffi.cast(rffi.VOIDP, x)        
+        x[0] = self._unwrap_object(space, w_obj)
+        return rffi.cast(rffi.VOIDP, x)
 
     def from_memory(self, space, w_obj, offset):
         fieldptr = self._get_fieldptr(space, w_obj, offset)
@@ -167,11 +189,18 @@
         floatptr[0] = r_singlefloat(space.float_w(w_value))
 
 class DoubleConverter(TypeConverter):
+    libffitype = libffi.types.double
+
+    def _unwrap_object(self, space, w_obj):
+        return space.float_w(w_obj)
+
     def convert_argument(self, space, w_obj):
-        arg = space.float_w(w_obj)
         x = lltype.malloc(rffi.DOUBLEP.TO, 1, flavor='raw')
-        x[0] = arg
-        return rffi.cast(rffi.VOIDP, x)        
+        x[0] = self._unwrap_object(space, w_obj)
+        return rffi.cast(rffi.VOIDP, x)
+
+    def convert_argument_libffi(self, space, w_obj, argchain):
+        argchain.arg(self._unwrap_object(space, w_obj))
 
     def from_memory(self, space, w_obj, offset):
         fieldptr = self._get_fieldptr(space, w_obj, offset)

Modified: pypy/branch/reflex-support/pypy/module/cppyy/executor.py
==============================================================================
--- pypy/branch/reflex-support/pypy/module/cppyy/executor.py	(original)
+++ pypy/branch/reflex-support/pypy/module/cppyy/executor.py	Thu Nov 25 17:23:22 2010
@@ -1,4 +1,5 @@
 from pypy.rpython.lltypesystem import rffi, lltype
+from pypy.rlib import libffi
 
 from pypy.module.cppyy import helper, capi
 
@@ -7,19 +8,29 @@
 
 class FunctionExecutor(object):
     _immutable_ = True
+    libffitype = libffi.types.NULL
+
     def __init__(self, space, cpptype):
         pass
 
     def execute(self, space, func, cppthis, num_args, args):
         raise NotImplementedError
 
+    def execute_libffi(self, space, libffifunc, argchain):
+        from pypy.module.cppyy.interp_cppyy import FastCallNotPossible
+        raise FastCallNotPossible
+
 
 class VoidExecutor(FunctionExecutor):
     _immutable_ = True
+
     def execute(self, space, func, cppthis, num_args, args):
         capi.c_call_v(func.cpptype.handle, func.method_index, cppthis, num_args, args)
         return space.w_None
 
+    def execute_libffi(self, space, libffifunc, argchain):
+        libffifunc.call(argchain, lltype.Void)
+        return space.w_None
 
 class BoolExecutor(FunctionExecutor):
     _immutable_ = True
@@ -29,34 +40,48 @@
 
 class CharExecutor(FunctionExecutor):
     _immutable_ = True
+
     def execute(self, space, func, cppthis, num_args, args):
         result = capi.c_call_c(func.cpptype.handle, func.method_index, cppthis, num_args, args)
         return space.wrap(result)
 
 class ShortExecutor(FunctionExecutor):
     _immutable_ = True
+    libffitype = libffi.types.sshort
+
     def execute(self, space, func, cppthis, num_args, args):
         result = capi.c_call_h(func.cpptype.handle, func.method_index, cppthis, num_args, args)
         return space.wrap(result)
 
 class LongExecutor(FunctionExecutor):
     _immutable_ = True
+    libffitype = libffi.types.slong
+
     def execute(self, space, func, cppthis, num_args, args):
         result = capi.c_call_l(func.cpptype.handle, func.method_index, cppthis, num_args, args)
         return space.wrap(result)
 
+    def execute_libffi(self, space, libffifunc, argchain):
+        return space.wrap(libffifunc.call(argchain, lltype.Signed))
+
 class FloatExecutor(FunctionExecutor):
     _immutable_ = True
+
     def execute(self, space, func, cppthis, num_args, args):
         result = capi.c_call_f(func.cpptype.handle, func.method_index, cppthis, num_args, args)
         return space.wrap(result)
 
 class DoubleExecutor(FunctionExecutor):
     _immutable_ = True
+    libffitype = libffi.types.double
+
     def execute(self, space, func, cppthis, num_args, args):
         result = capi.c_call_d(func.cpptype.handle, func.method_index, cppthis, num_args, args)
         return space.wrap(result)
 
+    def execute_libffi(self, space, libffifunc, argchain):
+        return space.wrap(libffifunc.call(argchain, rffi.DOUBLE))
+
 class CStringExecutor(FunctionExecutor):
     _immutable_ = True
     def execute(self, space, func, cppthis, num_args, args):

Modified: pypy/branch/reflex-support/pypy/module/cppyy/interp_cppyy.py
==============================================================================
--- pypy/branch/reflex-support/pypy/module/cppyy/interp_cppyy.py	(original)
+++ pypy/branch/reflex-support/pypy/module/cppyy/interp_cppyy.py	Thu Nov 25 17:23:22 2010
@@ -7,18 +7,18 @@
 
 from pypy.rpython.lltypesystem import rffi, lltype
 
-from pypy.rlib.libffi import CDLL
+from pypy.rlib import libffi
 from pypy.rlib import jit, debug
 
 from pypy.module.cppyy import converter, executor
 
-class HackCallNotPossible(Exception):
+class FastCallNotPossible(Exception):
     pass
 
 NULL_VOIDP = lltype.nullptr(rffi.VOIDP.TO)
 
 def load_lib(space, name):
-    cdll = CDLL(name)
+    cdll = libffi.CDLL(name)
     return W_CPPLibrary(space, cdll)
 load_lib.unwrap_spec = [ObjSpace, str]
 
@@ -72,18 +72,21 @@
         self.arg_types = arg_types
         self.executor = executor.get_executor(self.space, result_type)
         self.arg_converters = None
-        # <hack>
-        self.hack_call = arg_types == ['int'] and result_type == 'int'
-        # </hack>
+        methgetter = get_methptr_getter(self.cpptype.handle,
+                                        self.method_index)
+        self.methgetter = methgetter
+        self._libffifunc_cache = {}
 
     def call(self, cppthis, args_w):
         if self.executor is None:
             raise OperationError(self.space.w_TypeError, self.space.wrap("return type not handled"))
 
-        if self.hack_call:
+        if self.methgetter and cppthis: # only for methods
             try:
-                return self.do_hack_call(cppthis, args_w)
-            except HackCallNotPossible:
+                print "trying fast call"
+                return self.do_fast_call(cppthis, args_w)
+            except FastCallNotPossible:
+                print "failed"
                 pass
 
         args = self.prepare_arguments(args_w)
@@ -92,24 +95,44 @@
         finally:
             self.free_arguments(args)
 
-    INT_2_INT_FNPTR = lltype.Ptr(lltype.FuncType([rffi.VOIDP, rffi.INT],
-                                                 rffi.INT))
-    def do_hack_call(self, cppthis, args_w):
-        # hack: only for methods 'int m(int)'
+    @jit.unroll_safe
+    def do_fast_call(self, cppthis, args_w):
         space = self.space
-        if len(args_w) != 1:
+        # XXX factor out
+        if len(args_w) != len(self.arg_types):
             raise OperationError(space.w_TypeError, space.wrap("wrong number of args"))
-        arg = space.c_int_w(args_w[0])
-        methgetter = get_methptr_getter(self.cpptype.handle,
-                                        self.method_index)
-        if not methgetter:
-            raise HackCallNotPossible
-        funcptr = methgetter(cppthis)
-
-        funcptr = rffi.cast(self.INT_2_INT_FNPTR, funcptr)
-        funcptr = jit.hint(funcptr, promote=True)
-        result = funcptr(cppthis, arg)
-        return space.wrap(rffi.cast(lltype.Signed, result))
+        if self.arg_converters is None:
+            self._build_converters()
+        funcptr = jit.hint(self.methgetter, promote=True)(cppthis)
+        libffi_func = self._get_libffi_func(jit.hint(funcptr, promote=True))
+        if not libffi_func:
+            raise FastCallNotPossible
+
+        argchain = libffi.ArgChain()
+        argchain.arg(cppthis)
+        for i in range(len(args_w)):
+            conv = self.arg_converters[i]
+            w_arg = args_w[i]
+            conv.convert_argument_libffi(space, w_arg, argchain)
+        return self.executor.execute_libffi(space, libffi_func, argchain)
+
+    @jit.purefunction
+    def _get_libffi_func(self, funcptr):
+        key = rffi.cast(rffi.LONG, funcptr)
+        if key in self._libffifunc_cache:
+            return self._libffifunc_cache[key]
+        argtypes_libffi = [conv.libffitype for conv in self.arg_converters
+                              if conv.libffitype]
+        if (len(argtypes_libffi) == len(self.arg_converters) and
+                self.executor.libffitype):
+            # add c++ this to the arguments
+            libffifunc = libffi.Func("XXX",
+                                     [libffi.types.pointer] + argtypes_libffi,
+                                     self.executor.libffitype, funcptr)
+        else:
+            libffifunc = None
+        self._libffifunc_cache[key] = libffifunc
+        return libffifunc
 
     def _build_converters(self):
         self.arg_converters = [converter.get_converter(self.space, arg_type)
@@ -127,7 +150,7 @@
             conv = self.arg_converters[i]
             w_arg = args_w[i]
             try:
-                 arg = conv.convert_argument(space, w_arg)
+                arg = conv.convert_argument(space, w_arg)
             except:
                 # fun :-(
                 for j in range(i):
@@ -148,6 +171,9 @@
         return "CPPFunction(%s, %s, %r, %s)" % (
             self.cpptype, self.method_index, self.executor, self.arg_types)
 
+    def _freeze_(self):
+        assert 0, "you should never have a pre-built instance of this!"
+
 
 class CPPFunction(CPPMethod):
     def call(self, cppthis, args_w):

Modified: pypy/branch/reflex-support/pypy/rlib/libffi.py
==============================================================================
--- pypy/branch/reflex-support/pypy/rlib/libffi.py	(original)
+++ pypy/branch/reflex-support/pypy/rlib/libffi.py	Thu Nov 25 17:23:22 2010
@@ -64,6 +64,8 @@
         ## elif ffi_type is types.uint64:  return 'u'
         raise KeyError
 
+    NULL = lltype.nullptr(clibffi.FFI_TYPE_P.TO)
+
 types._import()
 
 @specialize.arg(0)
@@ -149,7 +151,7 @@
 
     _immutable_fields_ = ['funcsym']
     argtypes = []
-    restype = lltype.nullptr(clibffi.FFI_TYPE_P.TO)
+    restype = types.NULL
     funcsym = lltype.nullptr(rffi.VOIDP.TO)
 
     def __init__(self, name, argtypes, restype, funcsym, flags=FUNCFLAG_CDECL,
@@ -178,6 +180,7 @@
         # the optimizer will fail to recognize the pattern and won't turn it
         # into a fast CALL.  Note that "arg = arg.next" is optimized away,
         # assuming that archain is completely virtual.
+        self = jit.hint(self, promote=True)
         ll_args = self._prepare()
         i = 0
         arg = argchain.first



More information about the Pypy-commit mailing list