PageRenderTime 51ms CodeModel.GetById 19ms RepoModel.GetById 0ms app.codeStats 0ms

/rpython/jit/codewriter/test/test_call.py

https://bitbucket.org/pypy/pypy/
Python | 370 lines | 305 code | 55 blank | 10 comment | 27 complexity | 11e8121243a776e313e5a4e8d9a00e6b MD5 | raw file
Possible License(s): AGPL-3.0, BSD-3-Clause, Apache-2.0
  1. import py
  2. from rpython.flowspace.model import SpaceOperation, Constant, Variable
  3. from rpython.rtyper.lltypesystem import lltype, llmemory, rffi
  4. from rpython.translator.unsimplify import varoftype
  5. from rpython.rlib import jit
  6. from rpython.jit.codewriter import support, call
  7. from rpython.jit.codewriter.call import CallControl
  8. from rpython.jit.codewriter.effectinfo import EffectInfo
  9. class FakePolicy:
  10. def look_inside_graph(self, graph):
  11. return True
  12. def test_graphs_from_direct_call():
  13. cc = CallControl()
  14. F = lltype.FuncType([], lltype.Signed)
  15. f = lltype.functionptr(F, 'f', graph='fgraph')
  16. v = varoftype(lltype.Signed)
  17. op = SpaceOperation('direct_call', [Constant(f, lltype.Ptr(F))], v)
  18. #
  19. lst = cc.graphs_from(op, {}.__contains__)
  20. assert lst is None # residual call
  21. #
  22. lst = cc.graphs_from(op, {'fgraph': True}.__contains__)
  23. assert lst == ['fgraph'] # normal call
  24. def test_graphs_from_indirect_call():
  25. cc = CallControl()
  26. F = lltype.FuncType([], lltype.Signed)
  27. v = varoftype(lltype.Signed)
  28. graphlst = ['f1graph', 'f2graph']
  29. op = SpaceOperation('indirect_call', [varoftype(lltype.Ptr(F)),
  30. Constant(graphlst, lltype.Void)], v)
  31. #
  32. lst = cc.graphs_from(op, {'f1graph': True, 'f2graph': True}.__contains__)
  33. assert lst == ['f1graph', 'f2graph'] # normal indirect call
  34. #
  35. lst = cc.graphs_from(op, {'f1graph': True}.__contains__)
  36. assert lst == ['f1graph'] # indirect call, look only inside some graphs
  37. #
  38. lst = cc.graphs_from(op, {}.__contains__)
  39. assert lst is None # indirect call, don't look inside any graph
  40. def test_graphs_from_no_target():
  41. cc = CallControl()
  42. F = lltype.FuncType([], lltype.Signed)
  43. v = varoftype(lltype.Signed)
  44. op = SpaceOperation('indirect_call', [varoftype(lltype.Ptr(F)),
  45. Constant(None, lltype.Void)], v)
  46. lst = cc.graphs_from(op, {}.__contains__)
  47. assert lst is None
  48. # ____________________________________________________________
  49. class FakeJitDriverSD:
  50. def __init__(self, portal_graph):
  51. self.portal_graph = portal_graph
  52. self.portal_runner_ptr = "???"
  53. def test_find_all_graphs():
  54. def g(x):
  55. return x + 2
  56. def f(x):
  57. return g(x) + 1
  58. rtyper = support.annotate(f, [7])
  59. jitdriver_sd = FakeJitDriverSD(rtyper.annotator.translator.graphs[0])
  60. cc = CallControl(jitdrivers_sd=[jitdriver_sd])
  61. res = cc.find_all_graphs(FakePolicy())
  62. funcs = set([graph.func for graph in res])
  63. assert funcs == set([f, g])
  64. def test_find_all_graphs_without_g():
  65. def g(x):
  66. return x + 2
  67. def f(x):
  68. return g(x) + 1
  69. rtyper = support.annotate(f, [7])
  70. jitdriver_sd = FakeJitDriverSD(rtyper.annotator.translator.graphs[0])
  71. cc = CallControl(jitdrivers_sd=[jitdriver_sd])
  72. class CustomFakePolicy:
  73. def look_inside_graph(self, graph):
  74. assert graph.name == 'g'
  75. return False
  76. res = cc.find_all_graphs(CustomFakePolicy())
  77. funcs = [graph.func for graph in res]
  78. assert funcs == [f]
  79. # ____________________________________________________________
  80. def test_guess_call_kind_and_calls_from_graphs():
  81. class portal_runner_obj:
  82. graph = object()
  83. class FakeJitDriverSD:
  84. portal_runner_ptr = portal_runner_obj
  85. g = object()
  86. g1 = object()
  87. cc = CallControl(jitdrivers_sd=[FakeJitDriverSD()])
  88. cc.candidate_graphs = [g, g1]
  89. op = SpaceOperation('direct_call', [Constant(portal_runner_obj)],
  90. Variable())
  91. assert cc.guess_call_kind(op) == 'recursive'
  92. class fakeresidual:
  93. _obj = object()
  94. op = SpaceOperation('direct_call', [Constant(fakeresidual)],
  95. Variable())
  96. assert cc.guess_call_kind(op) == 'residual'
  97. class funcptr:
  98. class _obj:
  99. class graph:
  100. class func:
  101. oopspec = "spec"
  102. op = SpaceOperation('direct_call', [Constant(funcptr)],
  103. Variable())
  104. assert cc.guess_call_kind(op) == 'builtin'
  105. class funcptr:
  106. class _obj:
  107. graph = g
  108. op = SpaceOperation('direct_call', [Constant(funcptr)],
  109. Variable())
  110. res = cc.graphs_from(op)
  111. assert res == [g]
  112. assert cc.guess_call_kind(op) == 'regular'
  113. class funcptr:
  114. class _obj:
  115. graph = object()
  116. op = SpaceOperation('direct_call', [Constant(funcptr)],
  117. Variable())
  118. res = cc.graphs_from(op)
  119. assert res is None
  120. assert cc.guess_call_kind(op) == 'residual'
  121. h = object()
  122. op = SpaceOperation('indirect_call', [Variable(),
  123. Constant([g, g1, h])],
  124. Variable())
  125. res = cc.graphs_from(op)
  126. assert res == [g, g1]
  127. assert cc.guess_call_kind(op) == 'regular'
  128. op = SpaceOperation('indirect_call', [Variable(),
  129. Constant([h])],
  130. Variable())
  131. res = cc.graphs_from(op)
  132. assert res is None
  133. assert cc.guess_call_kind(op) == 'residual'
  134. # ____________________________________________________________
  135. def test_get_jitcode(monkeypatch):
  136. from rpython.jit.codewriter.test.test_flatten import FakeCPU
  137. class FakeRTyper:
  138. class annotator:
  139. translator = None
  140. def getfunctionptr(graph):
  141. F = lltype.FuncType([], lltype.Signed)
  142. return lltype.functionptr(F, 'bar')
  143. monkeypatch.setattr(call, 'getfunctionptr', getfunctionptr)
  144. cc = CallControl(FakeCPU(FakeRTyper()))
  145. class somegraph:
  146. name = "foo"
  147. jitcode = cc.get_jitcode(somegraph)
  148. assert jitcode is cc.get_jitcode(somegraph) # caching
  149. assert jitcode.name == "foo"
  150. pending = list(cc.enum_pending_graphs())
  151. assert pending == [(somegraph, jitcode)]
  152. # ____________________________________________________________
  153. def test_jit_force_virtualizable_effectinfo():
  154. py.test.skip("XXX add a test for CallControl.getcalldescr() -> EF_xxx")
  155. def test_releases_gil_analyzer():
  156. from rpython.jit.backend.llgraph.runner import LLGraphCPU
  157. T = rffi.CArrayPtr(rffi.TIME_T)
  158. external = rffi.llexternal("time", [T], rffi.TIME_T, releasegil=True)
  159. @jit.dont_look_inside
  160. def f():
  161. return external(lltype.nullptr(T.TO))
  162. rtyper = support.annotate(f, [])
  163. jitdriver_sd = FakeJitDriverSD(rtyper.annotator.translator.graphs[0])
  164. cc = CallControl(LLGraphCPU(rtyper), jitdrivers_sd=[jitdriver_sd])
  165. res = cc.find_all_graphs(FakePolicy())
  166. [f_graph] = [x for x in res if x.func is f]
  167. [block, _] = list(f_graph.iterblocks())
  168. [op] = block.operations
  169. call_descr = cc.getcalldescr(op)
  170. assert call_descr.extrainfo.has_random_effects()
  171. assert call_descr.extrainfo.is_call_release_gil() is False
  172. def test_call_release_gil():
  173. from rpython.jit.backend.llgraph.runner import LLGraphCPU
  174. T = rffi.CArrayPtr(rffi.TIME_T)
  175. external = rffi.llexternal("time", [T], rffi.TIME_T, releasegil=True,
  176. save_err=rffi.RFFI_SAVE_ERRNO)
  177. # no jit.dont_look_inside in this test
  178. def f():
  179. return external(lltype.nullptr(T.TO))
  180. rtyper = support.annotate(f, [])
  181. jitdriver_sd = FakeJitDriverSD(rtyper.annotator.translator.graphs[0])
  182. cc = CallControl(LLGraphCPU(rtyper), jitdrivers_sd=[jitdriver_sd])
  183. res = cc.find_all_graphs(FakePolicy())
  184. [llext_graph] = [x for x in res if x.func is external]
  185. [block, _] = list(llext_graph.iterblocks())
  186. [op] = block.operations
  187. tgt_tuple = op.args[0].value._obj.graph.func._call_aroundstate_target_
  188. assert type(tgt_tuple) is tuple and len(tgt_tuple) == 2
  189. call_target, saveerr = tgt_tuple
  190. assert saveerr == rffi.RFFI_SAVE_ERRNO
  191. call_target = llmemory.cast_ptr_to_adr(call_target)
  192. call_descr = cc.getcalldescr(op)
  193. assert call_descr.extrainfo.has_random_effects()
  194. assert call_descr.extrainfo.is_call_release_gil() is True
  195. assert call_descr.extrainfo.call_release_gil_target == (
  196. call_target, rffi.RFFI_SAVE_ERRNO)
  197. def test_random_effects_on_stacklet_switch():
  198. from rpython.jit.backend.llgraph.runner import LLGraphCPU
  199. from rpython.translator.platform import CompilationError
  200. try:
  201. from rpython.rlib._rffi_stacklet import switch, handle
  202. except CompilationError as e:
  203. if "Unsupported platform!" in e.out:
  204. py.test.skip("Unsupported platform!")
  205. else:
  206. raise e
  207. @jit.dont_look_inside
  208. def f():
  209. switch(rffi.cast(handle, 0))
  210. rtyper = support.annotate(f, [])
  211. jitdriver_sd = FakeJitDriverSD(rtyper.annotator.translator.graphs[0])
  212. cc = CallControl(LLGraphCPU(rtyper), jitdrivers_sd=[jitdriver_sd])
  213. res = cc.find_all_graphs(FakePolicy())
  214. [f_graph] = [x for x in res if x.func is f]
  215. [block, _] = list(f_graph.iterblocks())
  216. op = block.operations[-1]
  217. call_descr = cc.getcalldescr(op)
  218. assert call_descr.extrainfo.has_random_effects()
  219. def test_no_random_effects_for_rotateLeft():
  220. from rpython.jit.backend.llgraph.runner import LLGraphCPU
  221. from rpython.rlib.rarithmetic import r_uint
  222. if r_uint.BITS == 32:
  223. py.test.skip("64-bit only")
  224. from rpython.rlib.rmd5 import _rotateLeft
  225. def f(n, m):
  226. return _rotateLeft(r_uint(n), m)
  227. rtyper = support.annotate(f, [7, 9])
  228. jitdriver_sd = FakeJitDriverSD(rtyper.annotator.translator.graphs[0])
  229. cc = CallControl(LLGraphCPU(rtyper), jitdrivers_sd=[jitdriver_sd])
  230. res = cc.find_all_graphs(FakePolicy())
  231. [f_graph] = [x for x in res if x.func is f]
  232. [block, _] = list(f_graph.iterblocks())
  233. op = block.operations[-1]
  234. call_descr = cc.getcalldescr(op)
  235. assert not call_descr.extrainfo.has_random_effects()
  236. assert call_descr.extrainfo.check_is_elidable()
  237. def test_elidable_kinds():
  238. from rpython.jit.backend.llgraph.runner import LLGraphCPU
  239. @jit.elidable
  240. def f1(n, m):
  241. return n + m
  242. @jit.elidable
  243. def f2(n, m):
  244. return [n, m] # may raise MemoryError
  245. @jit.elidable
  246. def f3(n, m):
  247. if n > m:
  248. raise ValueError
  249. return n + m
  250. def f(n, m):
  251. a = f1(n, m)
  252. b = f2(n, m)
  253. c = f3(n, m)
  254. return a + len(b) + c
  255. rtyper = support.annotate(f, [7, 9])
  256. jitdriver_sd = FakeJitDriverSD(rtyper.annotator.translator.graphs[0])
  257. cc = CallControl(LLGraphCPU(rtyper), jitdrivers_sd=[jitdriver_sd])
  258. res = cc.find_all_graphs(FakePolicy())
  259. [f_graph] = [x for x in res if x.func is f]
  260. for index, expected in [
  261. (0, EffectInfo.EF_ELIDABLE_CANNOT_RAISE),
  262. (1, EffectInfo.EF_ELIDABLE_OR_MEMORYERROR),
  263. (2, EffectInfo.EF_ELIDABLE_CAN_RAISE)]:
  264. call_op = f_graph.startblock.operations[index]
  265. assert call_op.opname == 'direct_call'
  266. call_descr = cc.getcalldescr(call_op)
  267. assert call_descr.extrainfo.extraeffect == expected
  268. def test_raise_elidable_no_result():
  269. from rpython.jit.backend.llgraph.runner import LLGraphCPU
  270. l = []
  271. @jit.elidable
  272. def f1(n, m):
  273. l.append(n)
  274. def f(n, m):
  275. f1(n, m)
  276. return n + m
  277. rtyper = support.annotate(f, [7, 9])
  278. jitdriver_sd = FakeJitDriverSD(rtyper.annotator.translator.graphs[0])
  279. cc = CallControl(LLGraphCPU(rtyper), jitdrivers_sd=[jitdriver_sd])
  280. res = cc.find_all_graphs(FakePolicy())
  281. [f_graph] = [x for x in res if x.func is f]
  282. call_op = f_graph.startblock.operations[0]
  283. assert call_op.opname == 'direct_call'
  284. with py.test.raises(Exception):
  285. call_descr = cc.getcalldescr(call_op)
  286. def test_can_or_cannot_collect():
  287. from rpython.jit.backend.llgraph.runner import LLGraphCPU
  288. prebuilts = [[5], [6]]
  289. l = []
  290. def f1(n):
  291. if n > 1:
  292. raise IndexError
  293. return prebuilts[n] # cannot collect
  294. f1._dont_inline_ = True
  295. def f2(n):
  296. return [n] # can collect
  297. f2._dont_inline_ = True
  298. def f(n):
  299. a = f1(n)
  300. b = f2(n)
  301. return len(a) + len(b)
  302. rtyper = support.annotate(f, [1])
  303. jitdriver_sd = FakeJitDriverSD(rtyper.annotator.translator.graphs[0])
  304. cc = CallControl(LLGraphCPU(rtyper), jitdrivers_sd=[jitdriver_sd])
  305. res = cc.find_all_graphs(FakePolicy())
  306. [f_graph] = [x for x in res if x.func is f]
  307. for index, expected in [
  308. (0, False), # f1()
  309. (1, True), # f2()
  310. (2, False), # len()
  311. (3, False)]: # len()
  312. call_op = f_graph.startblock.operations[index]
  313. assert call_op.opname == 'direct_call'
  314. call_descr = cc.getcalldescr(call_op)
  315. assert call_descr.extrainfo.check_can_collect() == expected