PageRenderTime 43ms CodeModel.GetById 12ms RepoModel.GetById 0ms app.codeStats 0ms

/pypy/interpreter/astcompiler/optimize.py

https://bitbucket.org/pypy/pypy/
Python | 332 lines | 247 code | 44 blank | 41 comment | 57 complexity | 4c971e3f2531ef8676f3b5b22e5885d2 MD5 | raw file
Possible License(s): AGPL-3.0, BSD-3-Clause, Apache-2.0
  1. """codegen helpers and AST constant folding."""
  2. import sys
  3. from pypy.interpreter.astcompiler import ast, consts, misc
  4. from pypy.tool import stdlib_opcode as ops
  5. from pypy.interpreter.error import OperationError
  6. from rpython.rlib.unroll import unrolling_iterable
  7. from rpython.rlib.runicode import MAXUNICODE
  8. def optimize_ast(space, tree, compile_info):
  9. return tree.mutate_over(OptimizingVisitor(space, compile_info))
  10. CONST_NOT_CONST = -1
  11. CONST_FALSE = 0
  12. CONST_TRUE = 1
  13. class __extend__(ast.AST):
  14. def as_constant_truth(self, space):
  15. """Return the truth of this node if known."""
  16. const = self.as_constant()
  17. if const is None:
  18. return CONST_NOT_CONST
  19. return int(space.is_true(const))
  20. def as_constant(self):
  21. """Return the value of this node as a wrapped constant if possible."""
  22. return None
  23. def accept_jump_if(self, gen, condition, target):
  24. raise AssertionError("only for expressions")
  25. class __extend__(ast.expr):
  26. def accept_jump_if(self, gen, condition, target):
  27. self.walkabout(gen)
  28. if condition:
  29. gen.emit_jump(ops.POP_JUMP_IF_TRUE, target, True)
  30. else:
  31. gen.emit_jump(ops.POP_JUMP_IF_FALSE, target, True)
  32. class __extend__(ast.Num):
  33. def as_constant(self):
  34. return self.n
  35. class __extend__(ast.Str):
  36. def as_constant(self):
  37. return self.s
  38. class __extend__(ast.Const):
  39. def as_constant(self):
  40. return self.value
  41. class __extend__(ast.Index):
  42. def as_constant(self):
  43. return self.value.as_constant()
  44. class __extend__(ast.Slice):
  45. def as_constant(self):
  46. # XXX: this ought to return a slice object if all the indices are
  47. # constants, but we don't have a space here.
  48. return None
  49. class __extend__(ast.UnaryOp):
  50. def accept_jump_if(self, gen, condition, target):
  51. if self.op == ast.Not:
  52. self.operand.accept_jump_if(gen, not condition, target)
  53. else:
  54. ast.expr.accept_jump_if(self, gen, condition, target)
  55. class __extend__(ast.BoolOp):
  56. def _accept_jump_if_any_is(self, gen, condition, target, skip_last=0):
  57. for i in range(len(self.values) - skip_last):
  58. self.values[i].accept_jump_if(gen, condition, target)
  59. def accept_jump_if(self, gen, condition, target):
  60. if condition and self.op == ast.And or \
  61. (not condition and self.op == ast.Or):
  62. end = gen.new_block()
  63. self._accept_jump_if_any_is(gen, not condition, end, skip_last=1)
  64. self.values[-1].accept_jump_if(gen, condition, target)
  65. gen.use_next_block(end)
  66. else:
  67. self._accept_jump_if_any_is(gen, condition, target)
  68. def _binary_fold(name):
  69. def do_fold(space, left, right):
  70. return getattr(space, name)(left, right)
  71. return do_fold
  72. def _unary_fold(name):
  73. def do_fold(space, operand):
  74. return getattr(space, name)(operand)
  75. return do_fold
  76. def _fold_pow(space, w_left, w_right):
  77. # don't constant-fold if "w_left" and "w_right" are integers and
  78. # the estimated bit length of the power is unreasonably large
  79. space.appexec([w_left, w_right], """(left, right):
  80. if isinstance(left, (int, long)) and isinstance(right, (int, long)):
  81. if left.bit_length() * right > 5000:
  82. raise OverflowError
  83. """)
  84. return space.pow(w_left, w_right, space.w_None)
  85. def _fold_not(space, operand):
  86. return space.wrap(not space.is_true(operand))
  87. binary_folders = {
  88. ast.Add : _binary_fold("add"),
  89. ast.Sub : _binary_fold("sub"),
  90. ast.Mult : _binary_fold("mul"),
  91. ast.Div : _binary_fold("truediv"),
  92. ast.FloorDiv : _binary_fold("floordiv"),
  93. ast.Mod : _binary_fold("mod"),
  94. ast.Pow : _fold_pow,
  95. ast.LShift : _binary_fold("lshift"),
  96. ast.RShift : _binary_fold("rshift"),
  97. ast.BitOr : _binary_fold("or_"),
  98. ast.BitXor : _binary_fold("xor"),
  99. ast.BitAnd : _binary_fold("and_"),
  100. }
  101. unrolling_binary_folders = unrolling_iterable(binary_folders.items())
  102. unary_folders = {
  103. ast.Not : _fold_not,
  104. ast.USub : _unary_fold("neg"),
  105. ast.UAdd : _unary_fold("pos"),
  106. ast.Invert : _unary_fold("invert")
  107. }
  108. unrolling_unary_folders = unrolling_iterable(unary_folders.items())
  109. for folder in binary_folders.values() + unary_folders.values():
  110. folder._always_inline_ = 'try'
  111. del folder
  112. opposite_compare_operations = misc.dict_to_switch({
  113. ast.Is : ast.IsNot,
  114. ast.IsNot : ast.Is,
  115. ast.In : ast.NotIn,
  116. ast.NotIn : ast.In
  117. })
  118. class OptimizingVisitor(ast.ASTVisitor):
  119. """Constant folds AST."""
  120. def __init__(self, space, compile_info):
  121. self.space = space
  122. self.compile_info = compile_info
  123. def default_visitor(self, node):
  124. return node
  125. def visit_BinOp(self, binop):
  126. left = binop.left.as_constant()
  127. if left is not None:
  128. right = binop.right.as_constant()
  129. if right is not None:
  130. op = binop.op
  131. # Can't fold straight division without "from __future_ import
  132. # division" because it might be affected at runtime by the -Q
  133. # flag.
  134. if op == ast.Div and \
  135. not self.compile_info.flags & consts.CO_FUTURE_DIVISION:
  136. return binop
  137. try:
  138. for op_kind, folder in unrolling_binary_folders:
  139. if op_kind == op:
  140. w_const = folder(self.space, left, right)
  141. break
  142. else:
  143. raise AssertionError("unknown binary operation")
  144. # Let all errors be found at runtime.
  145. except OperationError:
  146. pass
  147. else:
  148. # To avoid blowing up the size of pyc files, we only fold
  149. # reasonably sized sequences.
  150. try:
  151. w_len = self.space.len(w_const)
  152. except OperationError:
  153. pass
  154. else:
  155. if self.space.int_w(w_len) > 20:
  156. return binop
  157. return ast.Const(w_const, binop.lineno, binop.col_offset)
  158. return binop
  159. def visit_UnaryOp(self, unary):
  160. w_operand = unary.operand.as_constant()
  161. op = unary.op
  162. if w_operand is not None:
  163. try:
  164. for op_kind, folder in unrolling_unary_folders:
  165. if op_kind == op:
  166. w_const = folder(self.space, w_operand)
  167. break
  168. else:
  169. raise AssertionError("unknown unary operation")
  170. w_minint = self.space.wrap(-sys.maxint - 1)
  171. # This makes sure the result is an integer.
  172. if self.space.eq_w(w_minint, w_const):
  173. w_const = w_minint
  174. except OperationError:
  175. pass
  176. else:
  177. return ast.Const(w_const, unary.lineno, unary.col_offset)
  178. elif op == ast.Not:
  179. compare = unary.operand
  180. if isinstance(compare, ast.Compare) and len(compare.ops) == 1:
  181. cmp_op = compare.ops[0]
  182. try:
  183. opposite = opposite_compare_operations(cmp_op)
  184. except KeyError:
  185. pass
  186. else:
  187. compare.ops[0] = opposite
  188. return compare
  189. return unary
  190. def visit_BoolOp(self, bop):
  191. values = bop.values
  192. we_are_and = bop.op == ast.And
  193. i = 0
  194. while i < len(values) - 1:
  195. truth = values[i].as_constant_truth(self.space)
  196. if truth != CONST_NOT_CONST:
  197. if (truth != CONST_TRUE) == we_are_and:
  198. del values[i + 1:]
  199. break
  200. else:
  201. del values[i]
  202. else:
  203. i += 1
  204. if len(values) == 1:
  205. return values[0]
  206. return bop
  207. def visit_Repr(self, rep):
  208. w_const = rep.value.as_constant()
  209. if w_const is not None:
  210. w_repr = self.space.repr(w_const)
  211. return ast.Const(w_repr, rep.lineno, rep.col_offset)
  212. return rep
  213. def visit_Name(self, name):
  214. # Turn loading None into a constant lookup. We cannot do this
  215. # for True and False, because rebinding them is allowed (2.7).
  216. if name.id == "None":
  217. # The compiler refuses to parse "None = ...", but "del None"
  218. # is allowed (if pointless). Check anyway: custom asts that
  219. # correspond to "None = ..." can be made by hand.
  220. if name.ctx == ast.Load:
  221. return ast.Const(self.space.w_None, name.lineno,
  222. name.col_offset)
  223. return name
  224. def visit_Tuple(self, tup):
  225. """Try to turn tuple building into a constant."""
  226. if tup.elts:
  227. consts_w = [None]*len(tup.elts)
  228. for i in range(len(tup.elts)):
  229. node = tup.elts[i]
  230. w_const = node.as_constant()
  231. if w_const is None:
  232. return tup
  233. consts_w[i] = w_const
  234. # intern the string constants packed into the tuple here,
  235. # because assemble.py will see the result as just a tuple constant
  236. for i in range(len(consts_w)):
  237. consts_w[i] = misc.intern_if_common_string(
  238. self.space, consts_w[i])
  239. else:
  240. consts_w = []
  241. w_consts = self.space.newtuple(consts_w)
  242. return ast.Const(w_consts, tup.lineno, tup.col_offset)
  243. def visit_Subscript(self, subs):
  244. if subs.ctx == ast.Load:
  245. w_obj = subs.value.as_constant()
  246. if w_obj is not None:
  247. w_idx = subs.slice.as_constant()
  248. if w_idx is not None:
  249. try:
  250. w_const = self.space.getitem(w_obj, w_idx)
  251. except OperationError:
  252. # Let exceptions propagate at runtime.
  253. return subs
  254. # CPython issue5057: if v is unicode, there might
  255. # be differences between wide and narrow builds in
  256. # cases like u'\U00012345'[0].
  257. # Wide builds will return a non-BMP char, whereas
  258. # narrow builds will return a surrogate. In both
  259. # the cases skip the optimization in order to
  260. # produce compatible pycs.
  261. if (self.space.isinstance_w(w_obj, self.space.w_unicode) and
  262. self.space.isinstance_w(w_const, self.space.w_unicode)):
  263. #unistr = self.space.unicode_w(w_const)
  264. #if len(unistr) == 1:
  265. # ch = ord(unistr[0])
  266. #else:
  267. # ch = 0
  268. #if (ch > 0xFFFF or
  269. # (MAXUNICODE == 0xFFFF and 0xD800 <= ch <= 0xDFFF)):
  270. # --XXX-- for now we always disable optimization of
  271. # u'...'[constant] because the tests above are not
  272. # enough to fix issue5057 (CPython has the same
  273. # problem as of April 24, 2012).
  274. # See test_const_fold_unicode_subscr
  275. return subs
  276. return ast.Const(w_const, subs.lineno, subs.col_offset)
  277. return subs