[pypy-svn] r50252 - in pypy/branch/astcompilertests/pypy/interpreter/astcompiler: . test

arigo at codespeak.net arigo at codespeak.net
Wed Jan 2 11:53:13 CET 2008


Author: arigo
Date: Wed Jan  2 11:53:12 2008
New Revision: 50252

Modified:
   pypy/branch/astcompilertests/pypy/interpreter/astcompiler/pycodegen.py
   pypy/branch/astcompilertests/pypy/interpreter/astcompiler/test/test_compiler.py
Log:
Avoids the intermediate tuple during tuple assignment in some common cases.


Modified: pypy/branch/astcompilertests/pypy/interpreter/astcompiler/pycodegen.py
==============================================================================
--- pypy/branch/astcompilertests/pypy/interpreter/astcompiler/pycodegen.py	(original)
+++ pypy/branch/astcompilertests/pypy/interpreter/astcompiler/pycodegen.py	Wed Jan  2 11:53:12 2008
@@ -5,7 +5,7 @@
 import sys
 
 from pypy.interpreter.astcompiler import ast
-from pypy.interpreter.astcompiler import pyassem, misc, future, symbols
+from pypy.interpreter.astcompiler import pyassem, misc, future, symbols, opt
 from pypy.interpreter.astcompiler.consts import SC_LOCAL, SC_GLOBAL, \
     SC_FREE, SC_CELL, SC_DEFAULT, OP_APPLY, OP_ASSIGN, OP_DELETE, OP_NONE
 from pypy.interpreter.astcompiler.consts import CO_VARARGS, CO_VARKEYWORDS, \
@@ -14,6 +14,7 @@
 from pypy.interpreter.pyparser.error import SyntaxError
 from pypy.interpreter.astcompiler.opt import is_constant_false
 from pypy.interpreter.astcompiler.opt import is_constant_true
+from pypy.interpreter.error import OperationError
 
 # drop VERSION dependency since it the ast transformer for 2.4 doesn't work with 2.3 anyway
 VERSION = 2
@@ -921,14 +922,82 @@
 
     def visitAssign(self, node):
         self.set_lineno(node)
+        if opt.OPTIMIZE and self._visitTupleAssignment(node):
+            return
         node.expr.accept( self )
         dups = len(node.nodes) - 1
         for i in range(len(node.nodes)):
             elt = node.nodes[i]
             if i < dups:
                 self.emit('DUP_TOP')
-            if isinstance(elt, ast.Node):
-                elt.accept( self )
+            assert isinstance(elt, ast.Node)
+            elt.accept( self )
+
+    def _visitTupleAssignment(self, parentnode):
+        # look for the assignment pattern (...) = (...)
+        space = self.space
+        expr = parentnode.expr
+        if isinstance(expr, ast.Tuple):
+            srcnodes = expr.nodes
+        elif isinstance(expr, ast.List):
+            srcnodes = expr.nodes
+        elif isinstance(expr, ast.Const):
+            try:
+                values_w = space.unpackiterable(expr.value)
+            except OperationError:
+                return False
+            srcnodes = [ast.Const(w) for w in values_w]
+        else:
+            return False
+        if len(parentnode.nodes) != 1:
+            return False
+        target = parentnode.nodes[0]
+        if not isinstance(target, ast.AssSeq):
+            return False
+        targetnodes = target.nodes
+        if len(targetnodes) != len(srcnodes):
+            return False
+        # we can only optimize two (common) particular cases, because
+        # the order of evaluation of the expression *and* the order
+        # of assignment should both be kept, in principle.
+        # 1. if all targetnodes are simple names, the assignment order
+        #    *should* not really matter.
+        # 2. otherwise, if the tuple is of length <= 3, we can emit simple
+        #    bytecodes to reverse the items in the value stack.
+        for node in targetnodes:
+            if not isinstance(node, ast.AssName):
+                break    # not a simple name
+        else:
+            # all simple names, case 1.
+            for node in srcnodes:
+                node.accept(self)
+            # let's be careful about the same name appearing several times
+            seen = {}
+            for i in range(len(targetnodes)-1, -1, -1):
+                node = targetnodes[i]
+                assert isinstance(node, ast.AssName)
+                if node.name not in seen:
+                    seen[node.name] = True
+                    self.storeName(node.name, node.lineno)
+                else:
+                    self.emit('POP_TOP')
+            return True  # done
+
+        n = len(srcnodes)
+        if n > 3:
+            return False    # can't do it
+        else:
+            # case 2.
+            for node in srcnodes:
+                node.accept(self)
+            if n == 2:
+                self.emit('ROT_TWO')
+            elif n == 3:
+                self.emit('ROT_THREE')
+                self.emit('ROT_TWO')
+            for node in targetnodes:
+                node.accept(self)
+            return True     # done
 
     def visitAssName(self, node):
         if node.flags == OP_ASSIGN:

Modified: pypy/branch/astcompilertests/pypy/interpreter/astcompiler/test/test_compiler.py
==============================================================================
--- pypy/branch/astcompilertests/pypy/interpreter/astcompiler/test/test_compiler.py	(original)
+++ pypy/branch/astcompilertests/pypy/interpreter/astcompiler/test/test_compiler.py	Wed Jan  2 11:53:12 2008
@@ -33,6 +33,7 @@
         source = str(py.code.Source(source))
         space = self.space
         code = compile_with_astcompiler(source, 'exec', space)
+        print
         code.dump()
         w_dict = space.newdict()
         code.exec_code(space, w_dict, w_dict)
@@ -78,6 +79,32 @@
         yield self.simple_test, "[x,y,z,t] = [1,2,3,4]", "x,y,z,t", (1, 2, 3,4)
         yield self.simple_test, "[x,y,x,t] = 1,2,3,4", "x,y,t", (3, 2, 4)
 
+    def test_tuple_assign_order(self):
+        decl = py.code.Source("""
+            class A:
+                def __getattr__(self, name):
+                    global seen
+                    seen += name
+                    return name
+                def __setattr__(self, name, value):
+                    global seen
+                    seen += '%s=%s' % (name, value)
+            seen = ''
+            a = A()
+        """)
+        decl = str(decl) + '\n'
+        yield self.st, decl+"a.x,= a.a,", 'seen', 'ax=a'
+        yield self.st, decl+"a.x,a.y = a.a,a.b", 'seen', 'abx=ay=b'
+        yield self.st, decl+"a.x,a.y,a.z = a.a,a.b,a.c", 'seen', 'abcx=ay=bz=c'
+        yield self.st, decl+"a.x,a.y,a.x,a.t = a.a,a.b,a.c,a.d", 'seen', \
+            'abcdx=ay=bx=ct=d'
+        yield self.st, decl+"[a.x] = [a.a]", 'seen', 'ax=a'
+        yield self.st, decl+"[a.x,a.y] = a.a,a.b", 'seen', 'abx=ay=b'
+        yield self.st, decl+"[a.x,a.y,a.z] = [a.a,a.b,a.c]", 'seen', \
+            'abcx=ay=bz=c'
+        yield self.st, decl+"[a.x,a.y,a.x,a.t] = a.a,a.b,a.c,a.d", 'seen', \
+            'abcdx=ay=bx=ct=d'
+
     def test_binary_operator(self):
         for operator in ['+', '-', '*', '**', '/', '&', '|', '^', '//',
                          '<<', '>>', 'and', 'or', '<', '>', '<=', '>=',



More information about the Pypy-commit mailing list