PageRenderTime 66ms CodeModel.GetById 32ms RepoModel.GetById 1ms app.codeStats 0ms

/pypy/translator/test/test_simplify.py

https://bitbucket.org/pypy/pypy/
Python | 448 lines | 389 code | 43 blank | 16 comment | 55 complexity | a22ecad854153f09fcda26c155b02810 MD5 | raw file
Possible License(s): AGPL-3.0, BSD-3-Clause, Apache-2.0
  1. import py
  2. from pypy.translator.translator import TranslationContext, graphof
  3. from pypy.translator.backendopt.all import backend_optimizations
  4. from pypy.translator.simplify import (get_graph, transform_dead_op_vars,
  5. desugar_isinstance)
  6. from pypy.objspace.flow.model import Block, Constant, summary
  7. from pypy import conftest
  8. def translate(func, argtypes, backend_optimize=True):
  9. t = TranslationContext()
  10. t.buildannotator().build_types(func, argtypes)
  11. t.buildrtyper().specialize()
  12. if backend_optimize:
  13. backend_optimizations(t)
  14. if conftest.option.view:
  15. t.view()
  16. return graphof(t, func), t
  17. def test_remove_ovfcheck_1():
  18. # check that ovfcheck() is handled
  19. from pypy.rlib.rarithmetic import ovfcheck
  20. def f(x):
  21. try:
  22. return ovfcheck(x*2)
  23. except OverflowError:
  24. return -42
  25. graph, _ = translate(f, [int])
  26. assert len(graph.startblock.operations) == 1
  27. assert graph.startblock.operations[0].opname == 'int_mul_ovf'
  28. assert len(graph.startblock.exits) == 2
  29. assert [link.target.operations for link in graph.startblock.exits] == \
  30. [(), ()]
  31. def test_remove_ovfcheck_bug():
  32. # check that ovfcheck() is correctly handled even if there is no
  33. # try:except: immediately around it
  34. from pypy.rlib.rarithmetic import ovfcheck
  35. def f(x):
  36. return ovfcheck(x*2) - 1
  37. graph, _ = translate(f, [int])
  38. assert len(graph.startblock.operations) == 2
  39. assert graph.startblock.operations[0].opname == 'int_mul_ovf'
  40. assert graph.startblock.operations[1].opname == 'int_sub'
  41. def test_remove_ovfcheck_floordiv():
  42. # check that ovfcheck() is handled even if the operation raises
  43. # and catches another exception too, here ZeroDivisionError
  44. from pypy.rlib.rarithmetic import ovfcheck
  45. def f(x, y):
  46. try:
  47. return ovfcheck(x // y)
  48. except OverflowError:
  49. return -42
  50. except ZeroDivisionError:
  51. return -43
  52. graph, _ = translate(f, [int, int])
  53. assert len(graph.startblock.operations) == 1
  54. assert graph.startblock.operations[0].opname == 'int_floordiv_ovf_zer'
  55. assert len(graph.startblock.exits) == 3
  56. assert [link.target.operations for link in graph.startblock.exits[1:]] == \
  57. [(), ()]
  58. def test_remove_ovfcheck_floordiv_2():
  59. # check that ovfcheck() is handled even if the operation raises
  60. # and catches only another exception, here ZeroDivisionError
  61. from pypy.rlib.rarithmetic import ovfcheck
  62. def f(x, y):
  63. try:
  64. return ovfcheck(x // y)
  65. except ZeroDivisionError:
  66. return -43
  67. graph, _ = translate(f, [int, int])
  68. assert len(graph.startblock.operations) == 1
  69. assert graph.startblock.operations[0].opname == 'int_floordiv_ovf_zer'
  70. assert len(graph.startblock.exits) == 3
  71. assert [link.target.operations for link in graph.startblock.exits[1:]] == \
  72. [(), ()]
  73. def test_remove_direct_call_without_side_effects():
  74. def f(x):
  75. return x + 123
  76. def g(x):
  77. a = f(x)
  78. return x * 12
  79. graph, _ = translate(g, [int])
  80. assert len(graph.startblock.operations) == 1
  81. def test_dont_remove_external_calls():
  82. import os
  83. def f(x):
  84. os.close(x)
  85. graph, _ = translate(f, [int])
  86. assert len(graph.startblock.operations) > 0
  87. def test_remove_recursive_call():
  88. def rec(a):
  89. if a <= 1:
  90. return 0
  91. else:
  92. return rec(a - 1) + 1
  93. def f(x):
  94. a = rec(x)
  95. return x + 12
  96. graph, _ = translate(f, [int])
  97. assert len(graph.startblock.operations) == 1
  98. def test_remove_call_with_indirect_call():
  99. def f1(x):
  100. return x + 1
  101. def f2(x):
  102. return x + 2
  103. def g(x):
  104. if x == 32:
  105. f = f1
  106. else:
  107. f = f2
  108. return f(x)
  109. def h(x):
  110. a = g(x)
  111. return x + 42
  112. graph, t = translate(h, [int])
  113. assert len(graph.startblock.operations) == 1
  114. def test_dont_remove_if_exception_guarded():
  115. def f(x):
  116. a = {} #do some stuff to prevent inlining
  117. a['123'] = 123
  118. a['1123'] = 1234
  119. return x + 1
  120. def g(x):
  121. try:
  122. a = f(x)
  123. except OverflowError:
  124. raise
  125. else:
  126. return 1
  127. graph, _ = translate(g, [int])
  128. assert graph.startblock.operations[-1].opname == 'direct_call'
  129. def test_remove_identical_variables():
  130. def g(code):
  131. pc = 0
  132. while pc < len(code):
  133. pc += 1
  134. return pc
  135. graph = TranslationContext().buildflowgraph(g)
  136. for block in graph.iterblocks():
  137. assert len(block.inputargs) <= 2 # at most 'pc' and 'code'
  138. def test_get_graph():
  139. import os
  140. def list_basic_ops(i, j):
  141. l = [1,2,3]
  142. l.insert(0, 42)
  143. del l[1]
  144. l.append(i)
  145. listlen = len(l)
  146. l.extend(l)
  147. del l[listlen:]
  148. l += [5,6]
  149. l[1] = i
  150. return l[j]
  151. def external_function():
  152. return os.system("ls")
  153. graph, t = translate(list_basic_ops, [int, int], False)
  154. for block in graph.iterblocks():
  155. for op in block.operations:
  156. if op.opname == "direct_call":
  157. print op
  158. graph = get_graph(op.args[0], t)
  159. assert graph is not None
  160. # an external function in RPython turns currently into
  161. # a call to a wrapper function which itself contains the
  162. # real call to a graph-less external ll function, so
  163. # we check recursively
  164. graph, t = translate(external_function, [], False)
  165. found = []
  166. def walkgraph(graph):
  167. for block in graph.iterblocks():
  168. for op in block.operations:
  169. if op.opname == "direct_call":
  170. print op
  171. subgraph = get_graph(op.args[0], t)
  172. if subgraph is None:
  173. found.append(op)
  174. else:
  175. walkgraph(subgraph)
  176. walkgraph(graph)
  177. assert len(found) == 1
  178. def addone(x):
  179. return x + 1
  180. def test_huge_func():
  181. g = None
  182. gstring = "def g(x):\n%s%s" % (" x = x + 1\n" * 1000, " return x\n")
  183. exec gstring
  184. assert g(1) == 1001
  185. # does not crash: previously join_blocks would barf on this
  186. graph, t = translate(g, [int])
  187. def test_join_blocks_cleans_links():
  188. from pypy.rpython.lltypesystem import lltype
  189. from pypy.objspace.flow.model import Constant
  190. from pypy.translator.backendopt.removenoops import remove_same_as
  191. def f(x):
  192. return bool(x + 2)
  193. def g(x):
  194. if f(x):
  195. return 1
  196. else:
  197. return 2
  198. graph, t = translate(g, [int], backend_optimize=False)
  199. fgraph = graphof(t, f)
  200. fgraph.startblock.exits[0].args = [Constant(True, lltype.Bool)]
  201. # does not crash: previously join_blocks would barf on this
  202. remove_same_as(graph)
  203. backend_optimizations(t)
  204. def test_transform_dead_op_vars_bug():
  205. from pypy.rpython.llinterp import LLInterpreter, LLException
  206. exc = ValueError()
  207. def f1():
  208. raise exc # this function used to be considered side-effects-free
  209. def f2():
  210. f1() # <- so this call was removed
  211. graph, t = translate(f2, [], backend_optimize=False)
  212. transform_dead_op_vars(graph, t)
  213. interp = LLInterpreter(t.rtyper)
  214. e = py.test.raises(LLException, 'interp.eval_graph(graph, [])')
  215. assert 'ValueError' in str(e.value)
  216. def test_desugar_isinstance():
  217. class X(object):
  218. pass
  219. def f():
  220. x = X()
  221. return isinstance(x, X())
  222. graph = TranslationContext().buildflowgraph(f)
  223. desugar_isinstance(graph)
  224. assert len(graph.startblock.operations) == 3
  225. block = graph.startblock
  226. assert block.operations[2].opname == "simple_call"
  227. assert isinstance(block.operations[2].args[0], Constant)
  228. assert block.operations[2].args[0].value is isinstance
  229. class TestDetectListComprehension:
  230. def check(self, f1, expected):
  231. t = TranslationContext(list_comprehension_operations=True)
  232. graph = t.buildflowgraph(f1)
  233. if conftest.option.view:
  234. graph.show()
  235. assert summary(graph) == expected
  236. def test_simple(self):
  237. def f1(l):
  238. return [x*17 for x in l]
  239. self.check(f1, {
  240. 'newlist': 1,
  241. 'iter': 1,
  242. 'next': 1,
  243. 'mul': 1,
  244. 'getattr': 1,
  245. 'simple_call': 1,
  246. 'hint': 2,
  247. })
  248. def test_with_exc(self):
  249. def g(x):
  250. return x * 17
  251. def free_some_stuff():
  252. pass
  253. def f1(l):
  254. try:
  255. return [g(x) for x in l]
  256. finally:
  257. free_some_stuff()
  258. self.check(f1, {
  259. 'newlist': 1,
  260. 'iter': 1,
  261. 'next': 1,
  262. 'getattr': 1,
  263. 'simple_call': 4,
  264. 'hint': 2,
  265. })
  266. def test_canraise_before_iter(self):
  267. def g(l):
  268. return l
  269. def f1(l):
  270. try:
  271. return [x*17 for x in g(l)]
  272. except ValueError:
  273. return []
  274. self.check(f1, {
  275. 'newlist': 2,
  276. 'iter': 1,
  277. 'next': 1,
  278. 'mul': 1,
  279. 'getattr': 1,
  280. 'simple_call': 2,
  281. 'hint': 2,
  282. })
  283. def test_iterate_over_list(self):
  284. def wrap(elem):
  285. return elem
  286. def f(i):
  287. new_l = []
  288. l = range(4)
  289. for elem in l:
  290. new_l.append(wrap(elem))
  291. return new_l
  292. self.check(f, {
  293. 'hint': 2,
  294. 'newlist': 1,
  295. 'iter': 1,
  296. 'next': 1,
  297. 'getattr': 1,
  298. 'simple_call': 3,
  299. })
  300. class TestLLSpecializeListComprehension:
  301. typesystem = 'lltype'
  302. def specialize(self, func, argtypes):
  303. from pypy.rpython.llinterp import LLInterpreter
  304. t = TranslationContext(list_comprehension_operations=True)
  305. t.buildannotator().build_types(func, argtypes)
  306. if conftest.option.view:
  307. t.view()
  308. t.buildrtyper(self.typesystem).specialize()
  309. backend_optimizations(t)
  310. if conftest.option.view:
  311. t.view()
  312. graph = graphof(t, func)
  313. interp = LLInterpreter(t.rtyper)
  314. return interp, graph
  315. def test_simple(self):
  316. def main(n):
  317. lst = [x*17 for x in range(n)]
  318. return lst[5]
  319. interp, graph = self.specialize(main, [int])
  320. res = interp.eval_graph(graph, [10])
  321. assert res == 5 * 17
  322. def test_simple_non_exact(self):
  323. def main(n):
  324. lst = [x*17 for x in range(n) if x < 5]
  325. return len(lst)
  326. interp, graph = self.specialize(main, [int])
  327. res = interp.eval_graph(graph, [10])
  328. assert res == 5
  329. def test_mutated_after_listcomp(self):
  330. def main(n):
  331. lst = [x*17 for x in range(n)]
  332. lst.append(-42)
  333. return lst[5]
  334. interp, graph = self.specialize(main, [int])
  335. res = interp.eval_graph(graph, [10])
  336. assert res == 5 * 17
  337. res = interp.eval_graph(graph, [5])
  338. assert res == -42
  339. def test_two_loops(self):
  340. def main(n, m):
  341. lst1 = []
  342. lst2 = []
  343. for i in range(n):
  344. lst1.append(i)
  345. for i in range(m):
  346. lst2.append(i)
  347. sum = 0
  348. for i in lst1:
  349. sum += i
  350. for i in lst2:
  351. sum -= i
  352. return sum
  353. interp, graph = self.specialize(main, [int, int])
  354. res = interp.eval_graph(graph, [8, 3])
  355. assert res == 28 - 3
  356. def test_dict(self):
  357. def main(n, m):
  358. d = {n: m, m: n}
  359. lst = [i*17 for i in d]
  360. return len(lst) + lst[0] + lst[-1]
  361. interp, graph = self.specialize(main, [int, int])
  362. res = interp.eval_graph(graph, [8, 5])
  363. assert res == 2 + 8 * 17 + 5 * 17
  364. res = interp.eval_graph(graph, [4, 4])
  365. assert res == 1 + 4 * 17 + 4 * 17
  366. def test_list_iterator(self):
  367. # for now, this is not optimized as a list comp
  368. def main(n):
  369. r = range(n)
  370. lst = [i*17 for i in iter(r)]
  371. return lst[5]
  372. interp, graph = self.specialize(main, [int])
  373. res = interp.eval_graph(graph, [8])
  374. assert res == 5 * 17
  375. def test_list_iterator_mutated_after_listcomp(self):
  376. # for now, this is not optimized as a list comp
  377. def main(n):
  378. r = range(n)
  379. lst = [i*17 for i in iter(r)]
  380. lst.append(42)
  381. return lst[5]
  382. interp, graph = self.specialize(main, [int])
  383. res = interp.eval_graph(graph, [8])
  384. assert res == 5 * 17
  385. def test_dict_iterator(self):
  386. # for now, this is not optimized as a list comp
  387. def main(n, m):
  388. d = {n: m, m: n}
  389. lst = [i*17 for i in d.iterkeys()]
  390. return len(lst) + lst[0] + lst[-1]
  391. interp, graph = self.specialize(main, [int, int])
  392. res = interp.eval_graph(graph, [8, 5])
  393. assert res == 2 + 8 * 17 + 5 * 17
  394. res = interp.eval_graph(graph, [4, 4])
  395. assert res == 1 + 4 * 17 + 4 * 17
  396. def test_iterate_over_constant(self):
  397. CONST = range(10)
  398. def main(n):
  399. lst = [x*17 for x in CONST]
  400. return lst[5]
  401. interp, graph = self.specialize(main, [int])
  402. res = interp.eval_graph(graph, [10])
  403. assert res == 5 * 17
  404. class TestOOSpecializeListComprehension(TestLLSpecializeListComprehension):
  405. typesystem = 'ootype'