[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