PageRenderTime 47ms CodeModel.GetById 21ms RepoModel.GetById 0ms app.codeStats 0ms

/rpython/rtyper/test/test_rvirtualizable.py

https://bitbucket.org/pypy/pypy/
Python | 372 lines | 303 code | 66 blank | 3 comment | 9 complexity | eea87c05ea2779532ef15b8bfb421875 MD5 | raw file
Possible License(s): AGPL-3.0, BSD-3-Clause, Apache-2.0
  1. import py
  2. from rpython.rtyper.llannotation import SomePtr
  3. from rpython.rtyper.lltypesystem import lltype, llmemory
  4. from rpython.rtyper.test.tool import BaseRtypingTest
  5. from rpython.rtyper.rvirtualizable import replace_force_virtualizable_with_call
  6. from rpython.rlib.jit import hint
  7. from rpython.flowspace.model import summary
  8. from rpython.rtyper.llinterp import LLInterpreter
  9. from rpython.rtyper.rclass import IR_IMMUTABLE, IR_IMMUTABLE_ARRAY
  10. from rpython.conftest import option
  11. class V(object):
  12. _virtualizable_ = ['v']
  13. v = -12
  14. w = -62
  15. def __init__(self, v):
  16. self.v = v
  17. self.w = v+1
  18. class SubclassV(V):
  19. pass
  20. class VArray(object):
  21. _virtualizable_ = ['lst[*]']
  22. def __init__(self, lst):
  23. self.lst = lst
  24. class B(object):
  25. _virtualizable_ = ['v0']
  26. x = "XX"
  27. def __init__(self, v0):
  28. self.v0 = v0
  29. def get_force_virtualizable_flags(graph):
  30. res = []
  31. for block, op in graph.iterblockops():
  32. if op.opname == 'jit_force_virtualizable':
  33. res.append(op.args[-1].value)
  34. return res
  35. class TestVirtualizable(BaseRtypingTest):
  36. prefix = 'inst_'
  37. GETFIELD = 'getfield'
  38. SETFIELD = 'setfield'
  39. def gettype(self, v):
  40. return v.concretetype.TO
  41. def test_generate_force_virtualizable(self):
  42. def fn(n):
  43. vinst = V(n)
  44. return vinst.v
  45. _, _, graph = self.gengraph(fn, [int])
  46. block = graph.startblock
  47. op_promote = block.operations[-2]
  48. op_getfield = block.operations[-1]
  49. assert op_getfield.opname == 'getfield'
  50. v_inst = op_getfield.args[0]
  51. assert op_promote.opname == 'jit_force_virtualizable'
  52. assert op_promote.args[0] is v_inst
  53. assert op_promote.args[-1].value == {}
  54. def test_generate_force_virtualizable_subclass(self):
  55. def fn(n):
  56. V(n) # to attach v to V
  57. vinst = SubclassV(n)
  58. return vinst.v
  59. _, _, graph = self.gengraph(fn, [int])
  60. block = graph.startblock
  61. op_promote = block.operations[-2]
  62. op_getfield = block.operations[-1]
  63. assert op_getfield.opname == 'getfield'
  64. v_inst = op_getfield.args[0]
  65. assert op_promote.opname == 'jit_force_virtualizable'
  66. assert op_promote.args[0] is v_inst
  67. assert op_promote.args[-1].value == {}
  68. def test_no_force_virtualizable_for_other_fields(self):
  69. def fn(n):
  70. vinst = V(n)
  71. return vinst.w
  72. _, _, graph = self.gengraph(fn, [int])
  73. block = graph.startblock
  74. op_getfield = block.operations[-1]
  75. op_call = block.operations[-2]
  76. assert op_getfield.opname == 'getfield'
  77. assert op_call.opname == 'direct_call' # to V.__init__
  78. def test_generate_force_virtualizable_array(self):
  79. def fn(n):
  80. vinst = VArray([n, n+1])
  81. return vinst.lst[1]
  82. _, _, graph = self.gengraph(fn, [int])
  83. block = graph.startblock
  84. op_promote = block.operations[-3]
  85. op_getfield = block.operations[-2]
  86. op_getarrayitem = block.operations[-1]
  87. assert op_getarrayitem.opname == 'direct_call' # to ll_getitem_xxx
  88. assert op_getfield.opname == 'getfield'
  89. v_inst = op_getfield.args[0]
  90. assert op_promote.opname == 'jit_force_virtualizable'
  91. assert op_promote.args[0] is v_inst
  92. assert op_promote.args[-1].value == {}
  93. def test_accessor(self):
  94. class Base(object):
  95. pass
  96. class V(Base):
  97. _virtualizable_ = ['v1', 'v2[*]']
  98. class W(V):
  99. pass
  100. #
  101. def fn1(n):
  102. Base().base1 = 42
  103. V().v1 = 43
  104. V().v2 = ['x', 'y']
  105. W().w1 = 44
  106. return V()
  107. _, _, graph = self.gengraph(fn1, [int])
  108. v_inst = graph.getreturnvar()
  109. TYPE = self.gettype(v_inst)
  110. accessor = TYPE._hints['virtualizable_accessor']
  111. assert accessor.TYPE == TYPE
  112. assert accessor.fields == {self.prefix + 'v1': IR_IMMUTABLE,
  113. self.prefix + 'v2': IR_IMMUTABLE_ARRAY}
  114. #
  115. def fn2(n):
  116. Base().base1 = 42
  117. V().v1 = 43
  118. V().v2 = ['x', 'y']
  119. W().w1 = 44
  120. return W()
  121. _, _, graph = self.gengraph(fn2, [int])
  122. w_inst = graph.getreturnvar()
  123. TYPE = self.gettype(w_inst)
  124. assert 'virtualizable_accessor' not in TYPE._hints
  125. def replace_force_virtualizable(self, rtyper, graphs):
  126. from rpython.annotator import model as annmodel
  127. from rpython.rtyper.annlowlevel import MixLevelHelperAnnotator
  128. graph = graphs[0]
  129. for block, op in graph.iterblockops():
  130. if op.opname == 'jit_force_virtualizable':
  131. v_inst_ll_type = op.args[0].concretetype
  132. break
  133. def mycall(vinst_ll):
  134. if vinst_ll.vable_token:
  135. raise ValueError
  136. annhelper = MixLevelHelperAnnotator(rtyper)
  137. s_vinst = SomePtr(v_inst_ll_type)
  138. funcptr = annhelper.delayedfunction(mycall, [s_vinst], annmodel.s_None)
  139. annhelper.finish()
  140. replace_force_virtualizable_with_call(graphs, v_inst_ll_type, funcptr)
  141. return funcptr
  142. def test_replace_force_virtualizable_with_call(self):
  143. def fn(n):
  144. vinst = V(n)
  145. return vinst.v
  146. _, rtyper, graph = self.gengraph(fn, [int])
  147. block = graph.startblock
  148. op_getfield = block.operations[-1]
  149. assert op_getfield.opname == 'getfield'
  150. funcptr = self.replace_force_virtualizable(rtyper, [graph])
  151. if getattr(option, 'view', False):
  152. graph.show()
  153. op_promote = block.operations[-2]
  154. op_getfield = block.operations[-1]
  155. assert op_getfield.opname == 'getfield'
  156. assert op_promote.opname == 'direct_call'
  157. assert op_promote.args[0].value == funcptr
  158. assert op_promote.args[1] == op_getfield.args[0]
  159. #
  160. interp = LLInterpreter(rtyper)
  161. res = interp.eval_graph(graph, [61])
  162. assert res == 61
  163. def test_access_directly(self):
  164. def g(b):
  165. b.v0 += 1
  166. return b.v0
  167. def f(n):
  168. b = B(n)
  169. b = hint(b, access_directly=True)
  170. return g(b)
  171. t, typer, graph = self.gengraph(f, [int])
  172. g_graph = t._graphof(g)
  173. expected = [{'access_directly': True}] * 3
  174. assert get_force_virtualizable_flags(g_graph) == expected
  175. self.replace_force_virtualizable(typer, [g_graph])
  176. assert summary(g_graph) == {self.GETFIELD: 2, self.SETFIELD: 1, 'int_add': 1}
  177. res = self.interpret(f, [23])
  178. assert res == 24
  179. def test_access_directly_exception(self):
  180. def g(b):
  181. return b.v0
  182. def f(n):
  183. b = B(n)
  184. b = hint(b, access_directly=True)
  185. if not b.v0:
  186. raise Exception
  187. return g(b)
  188. t, typer, graph = self.gengraph(f, [int])
  189. f_graph = t._graphof(f)
  190. g_graph = t._graphof(g)
  191. self.replace_force_virtualizable(typer, [f_graph, g_graph])
  192. t.checkgraphs()
  193. res = self.interpret(f, [23])
  194. assert res == 23
  195. def test_access_directly_specialized(self):
  196. def g(b):
  197. return b.v0
  198. def f(n):
  199. b = B(n)
  200. x = g(b)
  201. y = g(hint(b, access_directly=True))
  202. return x + y
  203. t, typer, graph = self.gengraph(f, [int])
  204. desc = typer.annotator.bookkeeper.getdesc(g)
  205. g_graphs = desc._cache.items()
  206. assert len(g_graphs) == 2
  207. g_graphs.sort()
  208. assert g_graphs[0][0] is None
  209. assert get_force_virtualizable_flags(g_graphs[0][1]) == [{}]
  210. expected = [{'access_directly': True}]
  211. assert get_force_virtualizable_flags(g_graphs[1][1]) == expected
  212. self.replace_force_virtualizable(typer, [g_graphs[0][1],
  213. g_graphs[1][1]])
  214. assert summary(g_graphs[0][1]) == {'direct_call': 1, self.GETFIELD: 1}
  215. assert summary(g_graphs[1][1]) == {self.GETFIELD: 1}
  216. res = self.interpret(f, [23])
  217. assert res == 46
  218. def test_access_directly_escape(self):
  219. class Global:
  220. pass
  221. glob = Global()
  222. def g(b):
  223. glob.b = b
  224. def h(b):
  225. return b.v0
  226. def f(n):
  227. b = B(n)
  228. g(b)
  229. g(hint(b, access_directly=True))
  230. return h(glob.b)
  231. t, typer, graph = self.gengraph(f, [int])
  232. desc = typer.annotator.bookkeeper.getdesc(g)
  233. g_graphs = desc._cache.items()
  234. assert len(g_graphs) == 2
  235. g_graphs.sort()
  236. assert g_graphs[0][0] is None
  237. assert summary(g_graphs[0][1]) == {self.SETFIELD: 1}
  238. assert summary(g_graphs[1][1]) == {self.SETFIELD: 1}
  239. h_graph = t._graphof(h)
  240. assert summary(h_graph) == {'jit_force_virtualizable': 1,
  241. self.GETFIELD: 1}
  242. assert get_force_virtualizable_flags(h_graph) == [{}]
  243. res = self.interpret(f, [23])
  244. assert res == 23
  245. def test_access_directly_method(self):
  246. class A:
  247. _virtualizable_ = ['v0']
  248. def __init__(self, v):
  249. self.v0 = v
  250. def meth1(self, x):
  251. return self.g(x+1)
  252. def g(self, y):
  253. return self.v0 * y
  254. def f(n):
  255. a = A(n)
  256. a = hint(a, access_directly=True)
  257. return a.meth1(100)
  258. t, typer, graph = self.gengraph(f, [int])
  259. g_graph = t._graphof(A.g.im_func)
  260. self.replace_force_virtualizable(typer, [g_graph])
  261. assert summary(g_graph) == {self.GETFIELD: 1, 'int_mul': 1}
  262. res = self.interpret(f, [23])
  263. assert res == 2323
  264. def test_access_directly_stop_at_dont_look_inside(self):
  265. from rpython.rlib.jit import dont_look_inside
  266. class A:
  267. _virtualizable_ = ['x']
  268. def h(a):
  269. g(a)
  270. h = dont_look_inside(h)
  271. def g(a):
  272. a.x = 2
  273. h(a)
  274. def f():
  275. a = A()
  276. a = hint(a, access_directly=True)
  277. a.x = 1
  278. g(a)
  279. t, typer, graph = self.gengraph(f, [])
  280. desc = typer.annotator.bookkeeper.getdesc(g)
  281. g_graphs = desc._cache.items()
  282. assert len(g_graphs) == 2
  283. g_graphs.sort()
  284. assert g_graphs[0][0] is None # default
  285. g_graph = g_graphs[0][1]
  286. g_graph_directly = g_graphs[1][1]
  287. f_graph = t._graphof(f)
  288. h_graph = t._graphof(h) # 1 graph!
  289. def get_direct_call_graph(graph):
  290. for block, op in graph.iterblockops():
  291. if op.opname == 'direct_call':
  292. return op.args[0].value._obj.graph
  293. return None
  294. assert get_direct_call_graph(f_graph) is g_graph_directly
  295. assert get_direct_call_graph(g_graph) is h_graph
  296. assert get_direct_call_graph(g_graph_directly) is h_graph
  297. assert get_direct_call_graph(h_graph) is g_graph
  298. def test_simple(self):
  299. def f(v):
  300. vinst = V(v)
  301. return vinst, vinst.v
  302. res = self.interpret(f, [42])
  303. assert res.item1 == 42
  304. res = lltype.normalizeptr(res.item0)
  305. assert res.inst_v == 42
  306. assert res.vable_token == lltype.nullptr(llmemory.GCREF.TO)