[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