PageRenderTime 49ms CodeModel.GetById 22ms RepoModel.GetById 1ms app.codeStats 0ms

/rpython/translator/test/test_simplify.py

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