[pypy-svn] r66549 - in pypy/branch/parser-compiler/pypy/interpreter: . astcompiler test
benjamin at codespeak.net
benjamin at codespeak.net
Thu Jul 23 18:54:35 CEST 2009
Author: benjamin
Date: Thu Jul 23 18:54:34 2009
New Revision: 66549
Modified:
pypy/branch/parser-compiler/pypy/interpreter/astcompiler/optimize.py
pypy/branch/parser-compiler/pypy/interpreter/pycompiler.py
pypy/branch/parser-compiler/pypy/interpreter/test/test_compiler.py
Log:
add a AST visitor which folds constants
Modified: pypy/branch/parser-compiler/pypy/interpreter/astcompiler/optimize.py
==============================================================================
--- pypy/branch/parser-compiler/pypy/interpreter/astcompiler/optimize.py (original)
+++ pypy/branch/parser-compiler/pypy/interpreter/astcompiler/optimize.py Thu Jul 23 18:54:34 2009
@@ -1,5 +1,14 @@
-from pypy.interpreter.astcompiler import ast2 as ast
+import sys
+import itertools
+
+from pypy.interpreter.astcompiler import ast2 as ast, consts, misc
from pypy.tool import stdlib_opcode as ops
+from pypy.interpreter.error import OperationError
+from pypy.rlib.unroll import unrolling_iterable
+
+
+def optimize_ast(space, tree, compile_info):
+ return tree.mutate_over(OptimizingVisitor(space, compile_info))
CONST_NOT_CONST = -1
@@ -38,6 +47,12 @@
return self.s
+class __extend__(ast.Const):
+
+ def as_constant(self):
+ return self.value
+
+
class __extend__(ast.UnaryOp):
def accept_jump_if(self, gen, condition, target):
@@ -65,3 +80,164 @@
gen.use_next_block(end)
else:
self._accept_jump_if_any_is(gen, condition, target)
+
+
+def _binary_fold(name):
+ def do_fold(space, left, right):
+ return getattr(space, name)(left, right)
+ return do_fold
+
+def _unary_fold(name):
+ def do_fold(space, operand):
+ return getattr(space, name)(operand)
+ return do_fold
+
+def _fold_pow(space, left, right):
+ return space.pow(left, right, space.w_None)
+
+def _fold_not(space, operand):
+ return space.wrap(not space.is_true(operand))
+
+
+binary_folders = {
+ ast.Add : _binary_fold("add"),
+ ast.Sub : _binary_fold("sub"),
+ ast.Mult : _binary_fold("mul"),
+ ast.Div : _binary_fold("truediv"),
+ ast.FloorDiv : _binary_fold("floordiv"),
+ ast.Mod : _binary_fold("mod"),
+ ast.Pow : _fold_pow,
+ ast.LShift : _binary_fold("lshift"),
+ ast.RShift : _binary_fold("rshift"),
+ ast.BitOr : _binary_fold("or_"),
+ ast.BitXor : _binary_fold("xor"),
+ ast.BitAnd : _binary_fold("and_")
+}
+unrolling_binary_folders = unrolling_iterable(binary_folders.items())
+
+unary_folders = {
+ ast.Not : _fold_not,
+ ast.USub : _unary_fold("neg"),
+ ast.UAdd : _unary_fold("pos"),
+ ast.Invert : _unary_fold("invert")
+}
+unrolling_unary_folders = unrolling_iterable(unary_folders.items())
+
+for folder in itertools.chain(binary_folders.itervalues(),
+ unary_folders.itervalues()):
+ folder._always_inline_ = True
+del folder
+
+opposite_compare_operations = misc.dict_to_switch({
+ ast.Is : ast.IsNot,
+ ast.IsNot : ast.Is,
+ ast.In : ast.NotIn,
+ ast.NotIn : ast.In
+})
+
+
+class OptimizingVisitor(ast.ASTVisitor):
+
+ def __init__(self, space, compile_info):
+ self.space = space
+ self.compile_info = compile_info
+
+ def default_visitor(self, node):
+ return node
+
+ def visit_BinOp(self, binop):
+ left = binop.left.as_constant()
+ if left is not None:
+ right = binop.right.as_constant()
+ if right is not None:
+ op = binop.op
+ if op == ast.Div and \
+ not self.compile_info.flags & consts.CO_FUTURE_DIVISION:
+ return binop
+ try:
+ for op_kind, folder in unrolling_binary_folders:
+ if op_kind == op:
+ w_const = folder(self.space, left, right)
+ except OperationError:
+ pass
+ else:
+ try:
+ w_len = self.space.len(w_const)
+ except OperationError:
+ pass
+ else:
+ if self.space.int_w(w_len) > 20:
+ return binop
+ return ast.Const(w_const, binop.lineno, binop.col_offset)
+ return binop
+
+ def visit_UnaryOp(self, unary):
+ w_operand = unary.operand.as_constant()
+ op = unary.op
+ if w_operand is not None:
+ try:
+ for op_kind, folder in unrolling_unary_folders:
+ if op_kind == op:
+ w_const = folder(self.space, w_operand)
+ w_minint = self.space.wrap(-sys.maxint - 1)
+ # This makes sure the result is an integer.
+ if self.space.eq_w(w_minint, w_const):
+ w_const = w_minint
+ except OperationError:
+ pass
+ else:
+ return ast.Const(w_const, unary.lineno, unary.col_offset)
+ elif op == ast.Not:
+ compare = unary.operand
+ if isinstance(compare, ast.Compare) and len(compare.ops) == 1:
+ cmp_op = compare.ops[0]
+ try:
+ opposite = opposite_compare_operations(cmp_op)
+ except KeyError:
+ pass
+ else:
+ compare.ops[0] = opposite
+ return compare
+ return unary
+
+ def visit_BoolOp(self, bop):
+ values = bop.values
+ we_are_and = bop.op == ast.And
+ i = 0
+ while i < len(values) - 1:
+ truth = values[i].as_constant_truth(self.space)
+ if truth != CONST_NOT_CONST:
+ if truth == CONST_FALSE == we_are_and:
+ del values[i + 1:]
+ break
+ else:
+ del values[i]
+ else:
+ i += 1
+ if len(values) == 1:
+ return values[0]
+ return bop
+
+ def visit_Repr(self, rep):
+ w_const = rep.value.as_constant()
+ if w_const is not None:
+ w_repr = self.space.repr(w_const)
+ return ast.Const(w_repr, rep.lineno, rep.col_offset)
+ return rep
+
+ def visit_Name(self, name):
+ if name.id == "None":
+ assert name.ctx == ast.Load
+ return ast.Const(self.space.w_None, name.lineno, name.col_offset)
+ return name
+
+ def visit_Tuple(self, tup):
+ consts_w = []
+ if tup.elts:
+ for node in tup.elts:
+ w_const = node.as_constant()
+ if w_const is None:
+ return tup
+ consts_w.append(w_const)
+ w_consts = self.space.newtuple(consts_w)
+ return ast.Const(w_consts, tup.lineno, tup.col_offset)
Modified: pypy/branch/parser-compiler/pypy/interpreter/pycompiler.py
==============================================================================
--- pypy/branch/parser-compiler/pypy/interpreter/pycompiler.py (original)
+++ pypy/branch/parser-compiler/pypy/interpreter/pycompiler.py Thu Jul 23 18:54:34 2009
@@ -231,7 +231,7 @@
from pypy.interpreter.pyparser.pythonlexer import TokenIndentationError
from pypy.interpreter.astcompiler.astbuilder import ast_from_node
from pypy.interpreter.astcompiler.codegen import compile_ast
- from pypy.interpreter.astcompiler import consts
+ from pypy.interpreter.astcompiler import consts, optimize
space = self.space
@@ -247,6 +247,7 @@
info = CompileInfo(filename, mode, flags, future_lineno)
parse_tree = self.parser.parse_source(source, info)
module = ast_from_node(space, parse_tree, info)
+ module = optimize.optimize_ast(space, module, info)
code = compile_ast(space, module, info)
except IndentationError, e:
raise OperationError(space.w_IndentationError,
Modified: pypy/branch/parser-compiler/pypy/interpreter/test/test_compiler.py
==============================================================================
--- pypy/branch/parser-compiler/pypy/interpreter/test/test_compiler.py (original)
+++ pypy/branch/parser-compiler/pypy/interpreter/test/test_compiler.py Thu Jul 23 18:54:34 2009
@@ -723,42 +723,6 @@
## py.test.skip("unsupported")
class AppTestOptimizer:
- def test_constant_fold_add(self):
- import parser
- class Folder(object):
- def defaultvisit(self, node):
- return node
-
- def __getattr__(self, attrname):
- if attrname.startswith('visit'):
- return self.defaultvisit
- raise AttributeError(attrname)
-
- def visitAdd(self, node):
- left = node.left
- right = node.right
- if isinstance(left, parser.ASTConst) and \
- isinstance(right, parser.ASTConst):
- if type(left.value) == type(right.value):
- return parser.ASTConst(left.value + right.value)
- return node
-
- def hook(ast, enc, filename):
- return ast.mutate(Folder())
-
- parser.install_compiler_hook(hook)
- code = compile("1+2", "", "eval")
- parser.install_compiler_hook(None)
- import dis, sys, StringIO
- s = StringIO.StringIO()
- so = sys.stdout
- sys.stdout = s
- try:
- dis.dis(code)
- finally:
- sys.stdout = so
- output = s.getvalue()
- assert 'BINARY_ADD' not in output
def test_remove_ending(self):
source = """def f():
@@ -777,7 +741,41 @@
output = s.getvalue()
assert output.count('LOAD_CONST') == 1
-
+ def test_none_constant(self):
+ import opcode
+ co = compile("def f(): return None", "<test>", "exec").co_consts[0]
+ co = co.co_code
+ op = ord(co[0]) + (ord(co[1]) << 8)
+ assert op == opcode.opmap["LOAD_CONST"]
+
+ def test_division_folding(self):
+ def code(source):
+ return compile(source, "<test>", "exec")
+ co = code("x = 10//4")
+ assert len(co.co_consts) == 2
+ assert co.co_consts[0] == 2
+ co = code("x = 10/4")
+ assert len(co.co_consts) == 3
+ assert co.co_consts[:2] == (10, 4)
+ co = code("from __future__ import division\nx = 10/4")
+ assert co.co_consts[2] == 2.5
+
+ def test_tuple_folding(self):
+ co = compile("(1, 2, 3)", "<test>", "exec")
+ assert co.co_consts == ((1, 2, 3), None)
+ co = compile("()", "<test>", "exec")
+ assert co.co_consts == ((), None)
+
+ def test_unary_folding(self):
+ co = compile("-(3)", "<test>", "exec")
+ assert co.co_consts[0] == -3
+ co = compile("~3", "<test>", "exec")
+ assert co.co_consts[0] == ~3
+ co = compile("+(-3)", "<test>", "exec")
+ assert co.co_consts[0] == -3
+ co = compile("not None", "<test>", "exec")
+ assert co.co_consts[0] is True
+
def test_folding_of_binops_on_constants(self):
def disassemble(func):
from StringIO import StringIO
More information about the Pypy-commit
mailing list