PageRenderTime 43ms CodeModel.GetById 17ms RepoModel.GetById 0ms app.codeStats 1ms

/rpython/translator/backendopt/test/test_inline.py

https://bitbucket.org/pypy/pypy/
Python | 661 lines | 604 code | 50 blank | 7 comment | 106 complexity | e5ec81597dc7aeea682d592738c8f516 MD5 | raw file
Possible License(s): AGPL-3.0, BSD-3-Clause, Apache-2.0
  1. # XXX clean up these tests to use more uniform helpers
  2. import py
  3. from rpython.flowspace.model import Variable, Constant, checkgraph
  4. from rpython.translator.backendopt import canraise
  5. from rpython.translator.backendopt.inline import (simple_inline_function,
  6. CannotInline, auto_inlining, Inliner, collect_called_graphs,
  7. measure_median_execution_cost, instrument_inline_candidates,
  8. auto_inline_graphs)
  9. from rpython.translator.translator import TranslationContext, graphof
  10. from rpython.rtyper.llinterp import LLInterpreter
  11. from rpython.rtyper.test.tool import BaseRtypingTest
  12. from rpython.rlib.rarithmetic import ovfcheck
  13. from rpython.translator.test.snippet import is_perfect_number
  14. from rpython.translator.backendopt.all import INLINE_THRESHOLD_FOR_TEST
  15. from rpython.conftest import option
  16. from rpython.translator.backendopt import removenoops
  17. from rpython.flowspace.model import summary
  18. def sanity_check(t):
  19. # look for missing '.concretetype'
  20. for graph in t.graphs:
  21. checkgraph(graph)
  22. for node in graph.iterblocks():
  23. for v in node.inputargs:
  24. assert hasattr(v, 'concretetype')
  25. for op in node.operations:
  26. for v in op.args:
  27. assert hasattr(v, 'concretetype')
  28. assert hasattr(op.result, 'concretetype')
  29. for node in graph.iterlinks():
  30. if node.exitcase is not None:
  31. assert hasattr(node, 'llexitcase')
  32. for v in node.args:
  33. assert hasattr(v, 'concretetype')
  34. if isinstance(node.last_exception, (Variable, Constant)):
  35. assert hasattr(node.last_exception, 'concretetype')
  36. if isinstance(node.last_exc_value, (Variable, Constant)):
  37. assert hasattr(node.last_exc_value, 'concretetype')
  38. class CustomError1(Exception):
  39. def __init__(self):
  40. self.data = 123
  41. class CustomError2(Exception):
  42. def __init__(self):
  43. self.data2 = 456
  44. class TestInline(BaseRtypingTest):
  45. def translate(self, func, argtypes):
  46. t = TranslationContext()
  47. t.buildannotator().build_types(func, argtypes)
  48. t.buildrtyper().specialize()
  49. return t
  50. def check_inline(self, func, in_func, sig, entry=None,
  51. inline_guarded_calls=False,
  52. graph=False):
  53. if entry is None:
  54. entry = in_func
  55. t = self.translate(entry, sig)
  56. # inline!
  57. sanity_check(t) # also check before inlining (so we don't blame it)
  58. if option.view:
  59. t.view()
  60. raise_analyzer = canraise.RaiseAnalyzer(t)
  61. inliner = Inliner(t, graphof(t, in_func), func,
  62. t.rtyper.lltype_to_classdef_mapping(),
  63. inline_guarded_calls,
  64. raise_analyzer=raise_analyzer)
  65. inliner.inline_all()
  66. if option.view:
  67. t.view()
  68. sanity_check(t)
  69. interp = LLInterpreter(t.rtyper)
  70. def eval_func(args):
  71. return interp.eval_graph(graphof(t, entry), args)
  72. if graph:
  73. return eval_func, graphof(t, func)
  74. return eval_func
  75. def check_auto_inlining(self, func, sig, multiplier=None, call_count_check=False,
  76. remove_same_as=False, heuristic=None, const_fold_first=False):
  77. t = self.translate(func, sig)
  78. if const_fold_first:
  79. from rpython.translator.backendopt.constfold import constant_fold_graph
  80. from rpython.translator.simplify import eliminate_empty_blocks
  81. for graph in t.graphs:
  82. constant_fold_graph(graph)
  83. eliminate_empty_blocks(graph)
  84. if option.view:
  85. t.view()
  86. # inline!
  87. sanity_check(t) # also check before inlining (so we don't blame it)
  88. threshold = INLINE_THRESHOLD_FOR_TEST
  89. if multiplier is not None:
  90. threshold *= multiplier
  91. call_count_pred = None
  92. if call_count_check:
  93. call_count_pred = lambda lbl: True
  94. instrument_inline_candidates(t.graphs, threshold)
  95. if remove_same_as:
  96. for graph in t.graphs:
  97. removenoops.remove_same_as(graph)
  98. if heuristic is not None:
  99. kwargs = {"heuristic": heuristic}
  100. else:
  101. kwargs = {}
  102. auto_inlining(t, threshold, call_count_pred=call_count_pred, **kwargs)
  103. sanity_check(t)
  104. if option.view:
  105. t.view()
  106. interp = LLInterpreter(t.rtyper)
  107. def eval_func(args):
  108. return interp.eval_graph(graphof(t, func), args)
  109. return eval_func, t
  110. def test_inline_simple(self):
  111. def f(x, y):
  112. return (g(x, y) + 1) * x
  113. def g(x, y):
  114. if x > 0:
  115. return x * y
  116. else:
  117. return -x * y
  118. eval_func = self.check_inline(g, f, [int, int])
  119. result = eval_func([-1, 5])
  120. assert result == f(-1, 5)
  121. result = eval_func([2, 12])
  122. assert result == f(2, 12)
  123. def test_nothing_to_inline(self):
  124. def f():
  125. return 1
  126. def g():
  127. return 2
  128. eval_func = self.check_inline(g, f, [])
  129. assert eval_func([]) == 1
  130. def test_inline_big(self):
  131. def f(x):
  132. result = []
  133. for i in range(1, x+1):
  134. if is_perfect_number(i):
  135. result.append(i)
  136. return result
  137. eval_func = self.check_inline(is_perfect_number, f, [int])
  138. result = eval_func([10])
  139. result = self.ll_to_list(result)
  140. assert len(result) == len(f(10))
  141. def test_inline_raising(self):
  142. def f(x):
  143. if x == 1:
  144. raise CustomError1
  145. return x
  146. def g(x):
  147. a = f(x)
  148. if x == 2:
  149. raise CustomError2
  150. def h(x):
  151. try:
  152. g(x)
  153. except CustomError1:
  154. return 1
  155. except CustomError2:
  156. return 2
  157. return x
  158. eval_func = self.check_inline(f,g, [int], entry=h)
  159. result = eval_func([0])
  160. assert result == 0
  161. result = eval_func([1])
  162. assert result == 1
  163. result = eval_func([2])
  164. assert result == 2
  165. def test_inline_several_times(self):
  166. def f(x):
  167. return (x + 1) * 2
  168. def g(x):
  169. if x:
  170. a = f(x) + f(x)
  171. else:
  172. a = f(x) + 1
  173. return a + f(x)
  174. eval_func = self.check_inline(f, g, [int])
  175. result = eval_func([0])
  176. assert result == g(0)
  177. result = eval_func([42])
  178. assert result == g(42)
  179. def test_always_inline(self):
  180. def f(x, y, z, k):
  181. p = (((x, y), z), k)
  182. return p[0][0][0] + p[-1]
  183. f._always_inline_ = True
  184. def g(x, y, z, k):
  185. a = f(x, y, z, k)
  186. return a
  187. eval_func, t = self.check_auto_inlining(g, [int, int, int, int], multiplier=0.1)
  188. graph = graphof(t, g)
  189. s = summary(graph)
  190. assert len(s) > 3
  191. def test_inline_exceptions(self):
  192. customError1 = CustomError1()
  193. customError2 = CustomError2()
  194. def f(x):
  195. if x == 0:
  196. raise customError1
  197. if x == 1:
  198. raise customError2
  199. def g(x):
  200. try:
  201. f(x)
  202. except CustomError1:
  203. return 2
  204. except CustomError2:
  205. return x+2
  206. return 1
  207. eval_func = self.check_inline(f, g, [int])
  208. result = eval_func([0])
  209. assert result == 2
  210. result = eval_func([1])
  211. assert result == 3
  212. result = eval_func([42])
  213. assert result == 1
  214. def test_inline_const_exceptions(self):
  215. valueError = ValueError()
  216. keyError = KeyError()
  217. def f(x):
  218. if x == 0:
  219. raise valueError
  220. if x == 1:
  221. raise keyError
  222. def g(x):
  223. try:
  224. f(x)
  225. except ValueError:
  226. return 2
  227. except KeyError:
  228. return x+2
  229. return 1
  230. eval_func = self.check_inline(f, g, [int])
  231. result = eval_func([0])
  232. assert result == 2
  233. result = eval_func([1])
  234. assert result == 3
  235. result = eval_func([42])
  236. assert result == 1
  237. def test_inline_exception_guarded(self):
  238. def h(x):
  239. if x == 1:
  240. raise CustomError1()
  241. elif x == 2:
  242. raise CustomError2()
  243. return 1
  244. def f(x):
  245. try:
  246. return h(x)
  247. except:
  248. return 87
  249. def g(x):
  250. try:
  251. return f(x)
  252. except CustomError1:
  253. return 2
  254. eval_func = self.check_inline(f, g, [int], inline_guarded_calls=True)
  255. result = eval_func([0])
  256. assert result == 1
  257. result = eval_func([1])
  258. assert result == 87
  259. result = eval_func([2])
  260. assert result == 87
  261. def test_inline_with_raising_non_call_op(self):
  262. class A:
  263. pass
  264. def f():
  265. return A()
  266. def g():
  267. try:
  268. a = f()
  269. except MemoryError:
  270. return 1
  271. return 2
  272. py.test.raises(CannotInline, self.check_inline, f, g, [])
  273. def test_inline_var_exception(self):
  274. def f(x):
  275. e = None
  276. if x == 0:
  277. e = CustomError1()
  278. elif x == 1:
  279. e = KeyError()
  280. if x == 0 or x == 1:
  281. raise e
  282. def g(x):
  283. try:
  284. f(x)
  285. except CustomError1:
  286. return 2
  287. except KeyError:
  288. return 3
  289. return 1
  290. eval_func, _ = self.check_auto_inlining(g, [int], multiplier=10)
  291. result = eval_func([0])
  292. assert result == 2
  293. result = eval_func([1])
  294. assert result == 3
  295. result = eval_func([42])
  296. assert result == 1
  297. def test_inline_nonraising_into_catching(self):
  298. def f(x):
  299. return x+1
  300. def g(x):
  301. try:
  302. return f(x)
  303. except KeyError:
  304. return 42
  305. eval_func = self.check_inline(f, g, [int])
  306. result = eval_func([7654])
  307. assert result == 7655
  308. def DONOTtest_call_call(self):
  309. # for reference. Just remove this test if we decide not to support
  310. # catching exceptions while inlining a graph that contains further
  311. # direct_calls.
  312. def e(x):
  313. if x < 0:
  314. raise KeyError
  315. return x+1
  316. def f(x):
  317. return e(x)+2
  318. def g(x):
  319. try:
  320. return f(x)+3
  321. except KeyError:
  322. return -1
  323. eval_func = self.check_inline(f, g, [int])
  324. result = eval_func([100])
  325. assert result == 106
  326. result = eval_func(g, [-100])
  327. assert result == -1
  328. def test_for_loop(self):
  329. def f(x):
  330. result = 0
  331. for i in range(0, x):
  332. result += i
  333. return result
  334. t = self.translate(f, [int])
  335. sanity_check(t) # also check before inlining (so we don't blame it)
  336. for graph in t.graphs:
  337. if graph.name.startswith('ll_rangenext'):
  338. break
  339. else:
  340. assert 0, "cannot find ll_rangenext_*() function"
  341. simple_inline_function(t, graph, graphof(t, f))
  342. sanity_check(t)
  343. interp = LLInterpreter(t.rtyper)
  344. result = interp.eval_graph(graphof(t, f), [10])
  345. assert result == 45
  346. def test_inline_constructor(self):
  347. class A:
  348. def __init__(self, x, y):
  349. self.bounds = (x, y)
  350. def area(self, height=10):
  351. return height * (self.bounds[1] - self.bounds[0])
  352. def f(i):
  353. a = A(117, i)
  354. return a.area()
  355. eval_func = self.check_inline(A.__init__.im_func, f, [int])
  356. result = eval_func([120])
  357. assert result == 30
  358. def test_cannot_inline_recursive_function(self):
  359. def factorial(n):
  360. if n > 1:
  361. return n * factorial(n-1)
  362. else:
  363. return 1
  364. def f(n):
  365. return factorial(n//2)
  366. py.test.raises(CannotInline, self.check_inline, factorial, f, [int])
  367. def test_auto_inlining_small_call_big(self):
  368. def leaf(n):
  369. total = 0
  370. i = 0
  371. while i < n:
  372. total += i
  373. if total > 100:
  374. raise OverflowError
  375. i += 1
  376. return total
  377. def g(n):
  378. return leaf(n)
  379. def f(n):
  380. try:
  381. return g(n)
  382. except OverflowError:
  383. return -1
  384. eval_func, t = self.check_auto_inlining(f, [int], multiplier=10)
  385. f_graph = graphof(t, f)
  386. assert len(collect_called_graphs(f_graph, t)) == 0
  387. result = eval_func([10])
  388. assert result == 45
  389. result = eval_func([15])
  390. assert result == -1
  391. def test_auto_inlining_small_call_big_call_count(self):
  392. def leaf(n):
  393. total = 0
  394. i = 0
  395. while i < n:
  396. total += i
  397. if total > 100:
  398. raise OverflowError
  399. i += 1
  400. return total
  401. def g(n):
  402. return leaf(n)
  403. def f(n):
  404. try:
  405. return g(n)
  406. except OverflowError:
  407. return -1
  408. eval_func, t = self.check_auto_inlining(f, [int], multiplier=10,
  409. call_count_check=True)
  410. f_graph = graphof(t, f)
  411. assert len(collect_called_graphs(f_graph, t)) == 0
  412. result = eval_func([10])
  413. assert result == 45
  414. result = eval_func([15])
  415. assert result == -1
  416. def test_inline_exception_catching(self):
  417. def f3():
  418. raise CustomError1
  419. def f2():
  420. try:
  421. f3()
  422. except CustomError1:
  423. return True
  424. else:
  425. return False
  426. def f():
  427. return f2()
  428. eval_func = self.check_inline(f2, f, [])
  429. result = eval_func([])
  430. assert result is True
  431. def test_inline_catching_different_exception(self):
  432. d = {1: 2}
  433. def f2(n):
  434. try:
  435. return ovfcheck(n+1)
  436. except OverflowError:
  437. raise
  438. def f(n):
  439. try:
  440. return f2(n)
  441. except ValueError:
  442. return -1
  443. eval_func = self.check_inline(f2, f, [int])
  444. result = eval_func([54])
  445. assert result == 55
  446. def test_inline_raiseonly(self):
  447. c = CustomError1()
  448. def f2(x):
  449. raise c
  450. def f(x):
  451. try:
  452. return f2(x)
  453. except CustomError1:
  454. return 42
  455. eval_func = self.check_inline(f2, f, [int])
  456. result = eval_func([98371])
  457. assert result == 42
  458. def test_measure_median_execution_cost(self):
  459. def f(x):
  460. x += 1
  461. x += 1
  462. x += 1
  463. while True:
  464. x += 1
  465. x += 1
  466. x += 1
  467. if x: break
  468. x += 1
  469. x += 1
  470. x += 1
  471. x += 1
  472. x += 1
  473. x += 1
  474. return x
  475. t = TranslationContext()
  476. graph = t.buildflowgraph(f)
  477. res = measure_median_execution_cost(graph)
  478. assert round(res, 5) == round(32.333333333, 5)
  479. def test_indirect_call_with_exception(self):
  480. class Dummy:
  481. pass
  482. def x1():
  483. return Dummy() # can raise MemoryError
  484. def x2():
  485. return None
  486. def x3(x):
  487. if x:
  488. f = x1
  489. else:
  490. f = x2
  491. return f()
  492. def x4():
  493. try:
  494. x3(0)
  495. x3(1)
  496. except CustomError2:
  497. return 0
  498. return 1
  499. assert x4() == 1
  500. py.test.raises(CannotInline, self.check_inline, x3, x4, [])
  501. def test_list_iteration(self):
  502. def f():
  503. tot = 0
  504. for item in [1,2,3]:
  505. tot += item
  506. return tot
  507. eval_func, t = self.check_auto_inlining(f, [])
  508. f_graph = graphof(t, f)
  509. called_graphs = collect_called_graphs(f_graph, t)
  510. assert len(called_graphs) == 0
  511. result = eval_func([])
  512. assert result == 6
  513. def test_bug_in_find_exception_type(self):
  514. def h():
  515. pass
  516. def g(i):
  517. if i > 0:
  518. raise IndexError
  519. else:
  520. h()
  521. def f(i):
  522. try:
  523. g(i)
  524. except IndexError:
  525. pass
  526. eval_func, t = self.check_auto_inlining(f, [int], remove_same_as=True,
  527. const_fold_first=True)
  528. eval_func([-66])
  529. eval_func([282])
  530. def test_correct_keepalive_placement(self):
  531. def h(x):
  532. if not x:
  533. raise ValueError
  534. return 1
  535. def f(x):
  536. s = "a %s" % (x, )
  537. try:
  538. h(len(s))
  539. except ValueError:
  540. pass
  541. return -42
  542. eval_func, t = self.check_auto_inlining(f, [int])
  543. res = eval_func([42])
  544. assert res == -42
  545. def test_keepalive_hard_case(self):
  546. from rpython.rtyper.lltypesystem import lltype
  547. Y = lltype.Struct('y', ('n', lltype.Signed))
  548. X = lltype.GcStruct('x', ('y', Y))
  549. def g(x):
  550. if x:
  551. return 3
  552. else:
  553. return 4
  554. def f():
  555. x = lltype.malloc(X)
  556. x.y.n = 2
  557. y = x.y
  558. z1 = g(y.n)
  559. z = y.n
  560. return z+z1
  561. eval_func = self.check_inline(g, f, [])
  562. res = eval_func([])
  563. assert res == 5
  564. def test_auto_inline_graphs_from_anywhere(self):
  565. def leaf(n):
  566. return n
  567. def f(n):
  568. return leaf(n)
  569. t = self.translate(f, [int])
  570. f_graph = graphof(t, f)
  571. assert len(collect_called_graphs(f_graph, t)) == 1
  572. auto_inline_graphs(t, [f_graph], 32)
  573. assert len(collect_called_graphs(f_graph, t)) == 1
  574. auto_inline_graphs(t, [f_graph], 32, inline_graph_from_anywhere=True)
  575. assert len(collect_called_graphs(f_graph, t)) == 0
  576. def test_inline_all(self):
  577. def g(x):
  578. return x + 1
  579. def f(x):
  580. return g(x) * g(x+1) * g(x+2) * g(x+3) * g(x+4) * g(x+5)
  581. t = self.translate(f, [int])
  582. sanity_check(t) # also check before inlining (so we don't blame it)
  583. simple_inline_function(t, graphof(t, g), graphof(t, f))
  584. sanity_check(t)
  585. assert summary(graphof(t, f)) == {'int_add': 11, 'int_mul': 5}
  586. interp = LLInterpreter(t.rtyper)
  587. result = interp.eval_graph(graphof(t, f), [10])
  588. assert result == f(10)
  589. def test_inline_all_exc(self):
  590. def g(x):
  591. if x < -100:
  592. raise ValueError
  593. return x + 1
  594. def f(x):
  595. n1 = g(x) * g(x+1)
  596. try:
  597. n2 = g(x+2) * g(x+3)
  598. except ValueError:
  599. n2 = 1
  600. n3 = g(x+4) * g(x+5)
  601. return n1 * n2 * n3
  602. t = self.translate(f, [int])
  603. sanity_check(t) # also check before inlining (so we don't blame it)
  604. simple_inline_function(t, graphof(t, g), graphof(t, f))
  605. sanity_check(t)
  606. assert summary(graphof(t, f)) == {'int_add': 11, 'int_mul': 5,
  607. 'cast_pointer': 12, 'getfield': 6,
  608. 'int_lt': 6}
  609. interp = LLInterpreter(t.rtyper)
  610. result = interp.eval_graph(graphof(t, f), [10])
  611. assert result == f(10)