[pypy-commit] pypy cpyext-fast-typecheck: WIP: modify the W_PyCWrapperObject call logic: instead of using a very generic logic which always build a tuple containing the arguments, we specify the expected arity and go through a specialized fast path

antocuni pypy.commits at gmail.com
Thu Mar 22 08:04:50 EDT 2018


Author: Antonio Cuni <anto.cuni at gmail.com>
Branch: cpyext-fast-typecheck
Changeset: r94066:ba7b2efa9585
Date: 2018-03-22 13:02 +0100
http://bitbucket.org/pypy/pypy/changeset/ba7b2efa9585/

Log:	WIP: modify the W_PyCWrapperObject call logic: instead of using a
	very generic logic which always build a tuple containing the
	arguments, we specify the expected arity and go through a
	specialized fast path

diff --git a/pypy/module/cpyext/methodobject.py b/pypy/module/cpyext/methodobject.py
--- a/pypy/module/cpyext/methodobject.py
+++ b/pypy/module/cpyext/methodobject.py
@@ -213,12 +213,14 @@
                             (self.name, self.w_objclass.getname(self.space)))
 
 
-
 class W_PyCWrapperObject(W_Root):
-    def __init__(self, space, pto, method_name, wrapper_func,
+    _immutable_fields_ = ["arity"]
+    
+    def __init__(self, space, pto, method_name, arity, wrapper_func,
                  wrapper_func_kwds, doc, func, offset=None):
         self.space = space
         self.method_name = method_name
+        self.arity = arity
         self.wrapper_func = wrapper_func
         self.wrapper_func_kwds = wrapper_func_kwds
         self.doc = doc
@@ -229,18 +231,10 @@
         assert isinstance(w_type, W_TypeObject)
         self.w_objclass = w_type
 
-    def descr_call(self, space, w_self, __args__):
-        args_w, kw_w = __args__.unpack()
-        w_args = space.newtuple(args_w)
-        w_kw = space.newdict()
-        for key, w_obj in kw_w.items():
-            space.setitem(w_kw, space.newtext(key), w_obj)
-        return self.call(space, w_self, w_args, w_kw)
-
-    def call(self, space, w_self, w_args, w_kw):
+    def _get_func_to_call(self):
         func_to_call = self.func
         if self.offset:
-            pto = as_pyobj(space, self.w_objclass)
+            pto = as_pyobj(self.space, self.w_objclass)
             # make ptr the equivalent of this, using the offsets
             #func_to_call = rffi.cast(rffi.VOIDP, ptr.c_tp_as_number.c_nb_multiply)
             if pto:
@@ -254,6 +248,36 @@
                 assert False, "failed to convert w_type %s to PyObject" % str(
                                                               self.w_objclass)
         assert func_to_call
+        return func_to_call
+
+    def descr_call(self, space, w_self, __args__):
+        if self.arity == -1:
+            # slow, fallback logic: eventually, this should be killed
+            args_w, kw_w = __args__.unpack()
+            w_args = space.newtuple(args_w)
+            w_kw = space.newdict()
+            for key, w_obj in kw_w.items():
+                space.setitem(w_kw, space.newtext(key), w_obj)
+            return self.call(space, w_self, w_args, w_kw)
+        #
+        # new logic
+        # XXX: check for keywords
+        length = len(__args__.arguments_w)
+        if length != self.arity:
+            raise oefmt(space.w_TypeError, "expected %d arguments, got %d",
+                        self.arity, length)
+        if self.arity == 1:
+            return self.call_1(space, w_self, __args__)
+
+        assert False, 'should not arrive here'
+
+    def call_1(self, space, w_self, __args__):
+        func = self._get_func_to_call()
+        w_o = __args__.arguments_w[0]
+        return self.wrapper_func(space, func, w_self, w_o)
+
+    def call(self, space, w_self, w_args, w_kw):
+        func_to_call = self._get_func_to_call()
         if self.wrapper_func is None:
             assert self.wrapper_func_kwds is not None
             return self.wrapper_func_kwds(space, w_self, w_args, func_to_call,
diff --git a/pypy/module/cpyext/slotdefs.py b/pypy/module/cpyext/slotdefs.py
--- a/pypy/module/cpyext/slotdefs.py
+++ b/pypy/module/cpyext/slotdefs.py
@@ -84,11 +84,9 @@
     check_num_args(space, w_args, 0)
     return generic_cpy_call(space, func_unary, w_self)
 
-def wrap_binaryfunc(space, w_self, w_args, func):
+def wrap_binaryfunc(space, func, w_self, w_x):
     func_binary = rffi.cast(binaryfunc, func)
-    check_num_args(space, w_args, 1)
-    args_w = space.fixedview(w_args)
-    return generic_cpy_call(space, func_binary, w_self, args_w[0])
+    return generic_cpy_call(space, func_binary, w_self, w_x)
 
 def _get_ob_type(space, w_obj):
     # please ensure that w_obj stays alive
diff --git a/pypy/module/cpyext/typeobject.py b/pypy/module/cpyext/typeobject.py
--- a/pypy/module/cpyext/typeobject.py
+++ b/pypy/module/cpyext/typeobject.py
@@ -338,7 +338,15 @@
             continue
         if wrapper_func is None and wrapper_func_kwds is None:
             continue
-        w_obj = W_PyCWrapperObject(space, pto, method_name, wrapper_func,
+
+        arity = -1
+        from pypy.module.cpyext.slotdefs import wrap_binaryfunc
+        if wrapper_func is wrap_binaryfunc:
+            # XXX: this is just a quick hack, we need an official way to
+            # specify the arity
+            arity = 1
+
+        w_obj = W_PyCWrapperObject(space, pto, method_name, arity, wrapper_func,
                 wrapper_func_kwds, doc, func_voidp, offset=offset)
         dict_w[method_name] = w_obj
     if pto.c_tp_doc:


More information about the pypy-commit mailing list