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

arigo at codespeak.net arigo at codespeak.net
Sun Dec 30 19:57:43 CET 2007


Author: arigo
Date: Sun Dec 30 19:57:42 2007
New Revision: 50221

Modified:
   pypy/branch/astcompilertests/pypy/interpreter/astcompiler/opt.py
   pypy/branch/astcompilertests/pypy/interpreter/astcompiler/test/test_compiler.py
   pypy/branch/astcompilertests/pypy/interpreter/pycompiler.py
Log:
More optimizations, mostly constant-folding.


Modified: pypy/branch/astcompilertests/pypy/interpreter/astcompiler/opt.py
==============================================================================
--- pypy/branch/astcompilertests/pypy/interpreter/astcompiler/opt.py	(original)
+++ pypy/branch/astcompilertests/pypy/interpreter/astcompiler/opt.py	Sun Dec 30 19:57:42 2007
@@ -1,8 +1,11 @@
 from pypy.tool.pairtype import extendabletype
 from pypy.interpreter.astcompiler import ast
+from pypy.interpreter.error import OperationError
 
-# Extra bytecode optimizations.  The visitor pattern is a bit of a mess
-# for these, so we simply stick new methods on the nodes.
+# Extra bytecode optimizations.  Two styles: a visitor (a mutator really)
+# that modifies the AST tree in-place, and hooks for pycodegen to produce
+# better bytecode.  The visitor pattern is a bit of a mess for the latter,
+# so we simply stick new methods on the nodes.
 
 OPTIMIZE = True
 
@@ -30,7 +33,11 @@
             codegen.emitop_block('JUMP_IF_FALSE', target)
 
 
-if OPTIMIZE:
+if not OPTIMIZE:
+    def optimize_ast_tree(space, tree):
+        return tree
+
+else:
 
     class __extend__(ast.Not):
         __metaclass__ = extendabletype
@@ -71,3 +78,169 @@
     class __extend__(ast.Or):
         __metaclass__ = extendabletype
         is_and = False
+
+
+    class OptimizerMutator(ast.ASTVisitor):
+        def __init__(self, space):
+            self.space = space
+
+        def default(self, node):
+            for child in node.getChildNodes():
+                child.accept(self)
+            return node
+
+        def _visitUnaryOp(self, node, constant_fold):
+            expr = node.expr
+            if isinstance(expr, ast.Const):
+                try:
+                    w_newvalue = constant_fold(self.space, expr.value)
+                except OperationError:
+                    pass
+                else:
+                    return ast.Const(w_newvalue)
+            return node
+
+        def visitBackquote(self, node):
+            return self._visitUnaryOp(node, _spacewrapper1('repr'))
+        def visitInvert(self, node):
+            return self._visitUnaryOp(node, _spacewrapper1('invert'))
+        def visitNot(self, node):
+            return self._visitUnaryOp(node, _spacewrapper1('not_'))
+        def visitUnaryAdd(self, node):
+            return self._visitUnaryOp(node, _spacewrapper1('pos'))
+        def visitUnarySub(self, node):
+            return self._visitUnaryOp(node, _spacewrapper1('neg'))
+
+        def _visitBinaryOp(self, node, constant_fold):
+            left = node.left
+            right = node.right
+            if isinstance(left, ast.Const) and isinstance(right, ast.Const):
+                try:
+                    w_newvalue = constant_fold(self.space, left.value,
+                                               right.value)
+                except OperationError:
+                    pass
+                else:
+                    # to avoid creating too large .pyc files, we don't
+                    # constant-fold operations that create long sequences,
+                    # like '(5,) * 500'.  This is the same as CPython.
+                    try:
+                        size = self.space.int_w(self.space.len(w_newvalue))
+                    except OperationError:
+                        size = -1
+                    if size <= 20:
+                        return ast.Const(w_newvalue)
+            return node
+
+        def visitAdd(self, node):
+            return self._visitBinaryOp(node, _spacewrapper2('add'))
+        #def visitDiv(self, node):
+        #    cannot constant-fold because the result depends on
+        #    whether future division is enabled or not
+        def visitFloorDiv(self, node):
+            return self._visitBinaryOp(node, _spacewrapper2('floordiv'))
+        def visitLeftShift(self, node):
+            return self._visitBinaryOp(node, _spacewrapper2('lshift'))
+        def visitMod(self, node):
+            return self._visitBinaryOp(node, _spacewrapper2('mod'))
+        def visitMul(self, node):
+            return self._visitBinaryOp(node, _spacewrapper2('mul'))
+        def visitPower(self, node):
+            return self._visitBinaryOp(node, constant_fold_pow)
+        def visitRightShift(self, node):
+            return self._visitBinaryOp(node, _spacewrapper2('rshift'))
+        def visitSub(self, node):
+            return self._visitBinaryOp(node, _spacewrapper2('sub'))
+
+        #def visitSubscript(self, node): XXX
+
+        def _visitBitOp(self, node, constant_fold):
+            while len(node.nodes) >= 2:
+                left = node.nodes[0]
+                if not isinstance(left, ast.Const):
+                    return node     # done
+                right = node.nodes[1]
+                if not isinstance(right, ast.Const):
+                    return node     # done
+                try:
+                    w_newvalue = constant_fold(self.space, left.value,
+                                               right.value)
+                except OperationError:
+                    return node     # done
+                del node.nodes[1]
+                node.nodes[0] = ast.Const(w_newvalue)
+            else:
+                # if reduced to a single node, just returns it
+                return node.nodes[0]
+
+        def visitBitand(self, node):
+            return self._visitBitOp(node, _spacewrapper2('and_'))
+        def visitBitor(self, node):
+            return self._visitBitOp(node, _spacewrapper2('or_'))
+        def visitBitxor(self, node):
+            return self._visitBitOp(node, _spacewrapper2('xor'))
+
+        #def visitCompare(self, node): XXX
+
+        def _visitAbstractTest(self, node, is_and):
+            # Logic for And nodes:
+            # 1. if any of the nodes is True, it can be removed
+            # 2. if any of the nodes is False, all nodes after it can be killed
+            # For Or nodes, the conditions are reversed.
+            i = 0
+            nodes = node.nodes
+            while i < len(nodes) - 1:
+                subnode = nodes[i]
+                if isinstance(subnode, ast.Const):
+                    if (not self.space.is_true(subnode.value)) == is_and:
+                        del nodes[i+1:]    # case 2.
+                        break
+                    else:
+                        del nodes[i]
+                        continue           # case 1.
+                i += 1
+            if len(nodes) > 1:
+                return node
+            else:
+                return nodes[0]       # a single item left
+
+        def visitAnd(self, node):
+            return self._visitAbstractTest(node, True)
+        def visitOr(self, node):
+            return self._visitAbstractTest(node, False)
+
+        def visitTuple(self, node):
+            consts_w = []
+            for subnode in node.nodes:
+                if not isinstance(subnode, ast.Const):
+                    return node     # not all constants
+                consts_w.append(subnode.value)
+            return ast.Const(self.space.newtuple(consts_w))
+
+
+    def _spacewrapper1(name):
+        """Make a wrapper around the method: space.<name>(w_x)
+        to avoid taking bound method objects, which creates issues
+        depending on the details of the real space method, e.g. default args.
+        """
+        def constant_fold(space, w_x):
+            return getattr(space, name)(w_x)
+        return constant_fold
+    _spacewrapper1._annspecialcase_ = 'specialize:memo'
+
+    def _spacewrapper2(name):
+        """Make a wrapper around the method: space.<name>(w_x, w_y)
+        to avoid taking bound method objects, which creates issues
+        depending on the details of the real space method, e.g. default args.
+        """
+        def constant_fold(space, w_x, w_y):
+            return getattr(space, name)(w_x, w_y)
+        return constant_fold
+    _spacewrapper2._annspecialcase_ = 'specialize:memo'
+
+    def constant_fold_pow(space, w_x, w_y):
+        return space.pow(w_x, w_y, space.w_None)
+
+
+    def optimize_ast_tree(space, tree):
+        return tree.mutate(OptimizerMutator(space))

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	Sun Dec 30 19:57:42 2007
@@ -1,5 +1,5 @@
 import py
-from pypy.interpreter.astcompiler import misc, pycodegen
+from pypy.interpreter.astcompiler import misc, pycodegen, opt
 from pypy.interpreter.pyparser.test.test_astbuilder import source2ast
 from pypy.interpreter.pyparser.test import expressions
 from pypy.interpreter.pycode import PyCode
@@ -7,6 +7,7 @@
 def compile_with_astcompiler(expr, mode, space):
     ast = source2ast(expr, mode, space)
     misc.set_filename('<testing>', ast)
+    ast = opt.optimize_ast_tree(space, ast)
     if mode == 'exec':
         Generator = pycodegen.ModuleCodeGenerator
     elif mode == 'single':

Modified: pypy/branch/astcompilertests/pypy/interpreter/pycompiler.py
==============================================================================
--- pypy/branch/astcompilertests/pypy/interpreter/pycompiler.py	(original)
+++ pypy/branch/astcompilertests/pypy/interpreter/pycompiler.py	Sun Dec 30 19:57:42 2007
@@ -233,6 +233,7 @@
         from pypy.interpreter.astcompiler.pycodegen import InteractiveCodeGenerator
         from pypy.interpreter.astcompiler.pycodegen import ExpressionCodeGenerator
         from pypy.interpreter.astcompiler.ast import Node
+        from pypy.interpreter.astcompiler import opt
         from pyparser.astbuilder import AstBuilder
         from pypy.interpreter.pycode import PyCode
         from pypy.interpreter.function import Function
@@ -254,6 +255,8 @@
             raise OperationError(space.w_SyntaxError,
                                  e.wrap_info(space, filename))
 
+        ast_tree = opt.optimize_ast_tree(space, ast_tree)
+
         if not space.is_w(self.w_compile_hook, space.w_None):
             try:
                 w_ast_tree = space.call_function(self.w_compile_hook,



More information about the Pypy-commit mailing list