[pypy-svn] r79172 - in pypy/trunk/pypy: interpreter interpreter/test module/array module/pypyjit/test

cfbolz at codespeak.net cfbolz at codespeak.net
Wed Nov 17 10:44:47 CET 2010


Author: cfbolz
Date: Wed Nov 17 10:44:46 2010
New Revision: 79172

Modified:
   pypy/trunk/pypy/interpreter/argument.py
   pypy/trunk/pypy/interpreter/test/test_argument.py
   pypy/trunk/pypy/module/array/interp_array.py
   pypy/trunk/pypy/module/pypyjit/test/test_pypy_c.py
Log:
svn merge -r79092:HEAD http://codespeak.net/svn/pypy/branch/jit-starargs

make the jit handle better calling functions with *args.


Modified: pypy/trunk/pypy/interpreter/argument.py
==============================================================================
--- pypy/trunk/pypy/interpreter/argument.py	(original)
+++ pypy/trunk/pypy/interpreter/argument.py	Wed Nov 17 10:44:46 2010
@@ -64,7 +64,7 @@
         return not self == other
 
 
-    # make it look tuply for the annotator
+    # make it look tuply for its use in the annotator
 
     def __len__(self):
         return 3
@@ -103,10 +103,11 @@
             make_sure_not_resized(self.keywords_w)
 
         make_sure_not_resized(self.arguments_w)
-        if ((w_stararg is not None and w_stararg) or
-            (w_starstararg is not None and w_starstararg)):
-            self._combine_wrapped(w_stararg, w_starstararg)
-            # if we have a call where * or ** args are used at the callsite
+        if w_stararg is not None and space.is_true(w_stararg):
+            self._combine_starargs_wrapped(w_stararg)
+        if w_starstararg is not None and space.is_true(w_starstararg):
+            self._combine_starstarargs_wrapped(w_starstararg)
+            # if we have a call where **args are used at the callsite
             # we shouldn't let the JIT see the argument matching
             self._dont_jit = True
         else:
@@ -142,42 +143,48 @@
 
     def _combine_wrapped(self, w_stararg, w_starstararg):
         "unpack the *arg and **kwd into arguments_w and keywords_w"
-        # unpack the * arguments 
         if w_stararg is not None:
-            self.arguments_w = (self.arguments_w +
-                                self.space.fixedview(w_stararg))
-        # unpack the ** arguments
+            self._combine_starargs_wrapped(w_stararg)
         if w_starstararg is not None:
-            space = self.space
-            if not space.is_true(space.isinstance(w_starstararg, space.w_dict)):
+            self._combine_starstarargs_wrapped(w_starstararg)
+
+    def _combine_starargs_wrapped(self, w_stararg):
+        # unpack the * arguments 
+        self.arguments_w = (self.arguments_w +
+                            self.space.fixedview(w_stararg))
+
+    def _combine_starstarargs_wrapped(self, w_starstararg):
+        # unpack the ** arguments
+        space = self.space
+        if not space.is_true(space.isinstance(w_starstararg, space.w_dict)):
+            raise OperationError(space.w_TypeError,
+                                 space.wrap("argument after ** must be "
+                                            "a dictionary"))
+        keywords_w = [None] * space.int_w(space.len(w_starstararg))
+        keywords = [None] * space.int_w(space.len(w_starstararg))
+        i = 0
+        for w_key in space.unpackiterable(w_starstararg):
+            try:
+                key = space.str_w(w_key)
+            except OperationError, e:
+                if not e.match(space, space.w_TypeError):
+                    raise
                 raise OperationError(space.w_TypeError,
-                                     space.wrap("argument after ** must be "
-                                                "a dictionary"))
-            keywords_w = [None] * space.int_w(space.len(w_starstararg))
-            keywords = [None] * space.int_w(space.len(w_starstararg))
-            i = 0
-            for w_key in space.unpackiterable(w_starstararg):
-                try:
-                    key = space.str_w(w_key)
-                except OperationError, e:
-                    if not e.match(space, space.w_TypeError):
-                        raise
-                    raise OperationError(space.w_TypeError,
-                                         space.wrap("keywords must be strings"))
-                if self.keywords and key in self.keywords:
-                    raise operationerrfmt(self.space.w_TypeError,
-                                          "got multiple values "
-                                          "for keyword argument "
-                                          "'%s'", key)
-                keywords[i] = key
-                keywords_w[i] = space.getitem(w_starstararg, w_key)
-                i += 1
-            if self.keywords is None:
-                self.keywords = keywords
-                self.keywords_w = keywords_w
-            else:
-                self.keywords = self.keywords + keywords
-                self.keywords_w = self.keywords_w + keywords_w
+                                     space.wrap("keywords must be strings"))
+            if self.keywords and key in self.keywords:
+                raise operationerrfmt(self.space.w_TypeError,
+                                      "got multiple values "
+                                      "for keyword argument "
+                                      "'%s'", key)
+            keywords[i] = key
+            keywords_w[i] = space.getitem(w_starstararg, w_key)
+            i += 1
+        if self.keywords is None:
+            self.keywords = keywords
+            self.keywords_w = keywords_w
+        else:
+            self.keywords = self.keywords + keywords
+            self.keywords_w = self.keywords_w + keywords_w
 
     def fixedunpack(self, argcount):
         """The simplest argument parsing: get the 'argcount' arguments,
@@ -226,6 +233,10 @@
         #   argnames = list of formal parameter names
         #   scope_w = resulting list of wrapped values
         #
+
+        # some comments about the JIT: it assumes that signature is a constant,
+        # so all values coming from there can be assumed constant. It assumes
+        # that the length of the defaults_w does not vary too much.
         co_argcount = signature.num_argnames() # expected formal arguments, without */**
         has_vararg = signature.has_vararg()
         has_kwarg = signature.has_kwarg()
@@ -245,12 +256,6 @@
         args_w = self.arguments_w
         num_args = len(args_w)
 
-        keywords = self.keywords
-        keywords_w = self.keywords_w
-        num_kwds = 0
-        if keywords is not None:
-            num_kwds = len(keywords)
-
         avail = num_args + upfront
 
         if input_argcount < co_argcount:
@@ -260,15 +265,24 @@
             else:
                 take = num_args
 
+            # letting the JIT unroll this loop is safe, because take is always
+            # smaller than co_argcount
             for i in range(take):
                 scope_w[i + input_argcount] = args_w[i]
             input_argcount += take
 
+        keywords = self.keywords
+        keywords_w = self.keywords_w
+        num_kwds = 0
+        if keywords is not None:
+            num_kwds = len(keywords)
         # the code assumes that keywords can potentially be large, but that
         # argnames is typically not too large
         num_remainingkwds = num_kwds
         used_keywords = None
         if keywords:
+            # letting JIT unroll the loop is *only* safe if the callsite didn't
+            # use **args because num_kwds can be arbitrarily large otherwise.
             used_keywords = [False] * num_kwds
             for i in range(num_kwds):
                 name = keywords[i]
@@ -276,7 +290,7 @@
                 if j < 0:
                     continue
                 elif j < input_argcount:
-                    # check that no keyword argument conflicts with these note
+                    # check that no keyword argument conflicts with these. note
                     # that for this purpose we ignore the first blindargs,
                     # which were put into place by prepend().  This way,
                     # keywords do not conflict with the hidden extra argument

Modified: pypy/trunk/pypy/interpreter/test/test_argument.py
==============================================================================
--- pypy/trunk/pypy/interpreter/test/test_argument.py	(original)
+++ pypy/trunk/pypy/interpreter/test/test_argument.py	Wed Nov 17 10:44:46 2010
@@ -52,12 +52,17 @@
         assert y == "d"
         assert z == "e"
 
+class dummy_wrapped_dict(dict):
+    def __nonzero__(self):
+        raise NotImplementedError
 
 class DummySpace(object):
     def newtuple(self, items):
         return tuple(items)
 
     def is_true(self, obj):
+        if isinstance(obj, dummy_wrapped_dict):
+            return bool(dict(obj))
         return bool(obj)
 
     def fixedview(self, it):
@@ -229,7 +234,7 @@
             kwds_w = dict(kwds[:i])
             keywords = kwds_w.keys()
             keywords_w = kwds_w.values()
-            w_kwds = dict(kwds[i:])
+            w_kwds = dummy_wrapped_dict(kwds[i:])
             if i == 2:
                 w_kwds = None
             assert len(keywords) == len(keywords_w)
@@ -265,7 +270,7 @@
             kwds_w = dict(kwds[:i])
             keywords = kwds_w.keys()
             keywords_w = kwds_w.values()
-            w_kwds = dict(kwds[i:])
+            w_kwds = dummy_wrapped_dict(kwds[i:])
             if i == 3:
                 w_kwds = None
             args = Arguments(space, [1, 2], keywords, keywords_w, w_starstararg=w_kwds)

Modified: pypy/trunk/pypy/module/array/interp_array.py
==============================================================================
--- pypy/trunk/pypy/module/array/interp_array.py	(original)
+++ pypy/trunk/pypy/module/array/interp_array.py	Wed Nov 17 10:44:46 2010
@@ -27,7 +27,7 @@
     typecode = typecode[0]
 
     if space.is_w(w_cls, space.gettypeobject(W_ArrayBase.typedef)):
-        if len(w_args.keywords_w) > 0:
+        if w_args.keywords: # XXX this might be forbidden fishing
             msg = 'array.array() does not take keyword arguments'
             raise OperationError(space.w_TypeError, space.wrap(msg))
         

Modified: pypy/trunk/pypy/module/pypyjit/test/test_pypy_c.py
==============================================================================
--- pypy/trunk/pypy/module/pypyjit/test/test_pypy_c.py	(original)
+++ pypy/trunk/pypy/module/pypyjit/test/test_pypy_c.py	Wed Nov 17 10:44:46 2010
@@ -377,10 +377,75 @@
                     ([1000], 49500),
                     ([10000], 495000),
                     ([100000], 4950000))
-        assert len(self.loops) == 2
+        assert len(self.loops) == 3
         op, = self.get_by_bytecode("CALL_FUNCTION_KW")
         # XXX a bit too many guards, but better than before
-        assert len(op.get_opnames("guard")) <= 10
+        assert len(op.get_opnames("guard")) <= 12
+
+    def test_stararg_virtual(self):
+        self.run_source('''
+            d = {}
+
+            def g(*args):
+                return len(args)
+            def h(a, b, c):
+                return c
+
+            def main(x):
+                s = 0
+                for i in range(x):
+                    l = [i, x, 2]
+                    s += g(*l)
+                    s += h(*l)
+                    s += g(i, x, 2)
+                for i in range(x):
+                    l = [x, 2]
+                    s += g(i, *l)
+                    s += h(i, *l)
+                return s
+        ''', 100000, ([100], 1300),
+                    ([1000], 13000),
+                    ([10000], 130000),
+                    ([100000], 1300000))
+        assert len(self.loops) == 2
+        ops = self.get_by_bytecode("CALL_FUNCTION_VAR")
+        assert len(ops) == 4
+        for op in ops:
+            assert len(op.get_opnames("new")) == 0
+            assert len(op.get_opnames("call_may_force")) == 0
+
+        ops = self.get_by_bytecode("CALL_FUNCTION")
+        for op in ops:
+            assert len(op.get_opnames("new")) == 0
+            assert len(op.get_opnames("call_may_force")) == 0
+
+    def test_stararg(self):
+        self.run_source('''
+            d = {}
+
+            def g(*args):
+                return args[-1]
+            def h(*args):
+                return len(args)
+
+            def main(x):
+                s = 0
+                l = []
+                i = 0
+                while i < x:
+                    l.append(1)
+                    s += g(*l)
+                    i = h(*l)
+                return s
+        ''', 100000, ([100], 100),
+                     ([1000], 1000),
+                     ([2000], 2000),
+                     ([4000], 4000))
+        assert len(self.loops) == 1
+        ops = self.get_by_bytecode("CALL_FUNCTION_VAR")
+        for op in ops:
+            assert len(op.get_opnames("new_with_vtable")) == 0
+            assert len(op.get_opnames("call_may_force")) == 0
 
     def test_virtual_instance(self):
         self.run_source('''



More information about the Pypy-commit mailing list