PageRenderTime 46ms CodeModel.GetById 17ms RepoModel.GetById 0ms app.codeStats 0ms

/rpython/jit/codewriter/call.py

https://bitbucket.org/pypy/pypy/
Python | 370 lines | 327 code | 17 blank | 26 comment | 60 complexity | 8d5184a5831b713917ad50853c69577b MD5 | raw file
Possible License(s): AGPL-3.0, BSD-3-Clause, Apache-2.0
  1. #
  2. # Contains the logic to decide, based on the policy, which graphs
  3. # to transform to JitCodes or not.
  4. #
  5. from rpython.jit.codewriter import support
  6. from rpython.jit.codewriter.jitcode import JitCode
  7. from rpython.jit.codewriter.effectinfo import (VirtualizableAnalyzer,
  8. QuasiImmutAnalyzer, RandomEffectsAnalyzer, effectinfo_from_writeanalyze,
  9. EffectInfo, CallInfoCollection)
  10. from rpython.rtyper.lltypesystem import lltype, llmemory
  11. from rpython.rtyper.lltypesystem.lltype import getfunctionptr
  12. from rpython.rlib import rposix
  13. from rpython.translator.backendopt.canraise import RaiseAnalyzer
  14. from rpython.translator.backendopt.writeanalyze import ReadWriteAnalyzer
  15. from rpython.translator.backendopt.graphanalyze import DependencyTracker
  16. from rpython.translator.backendopt.collectanalyze import CollectAnalyzer
  17. class CallControl(object):
  18. virtualref_info = None # optionally set from outside
  19. has_libffi_call = False # default value
  20. def __init__(self, cpu=None, jitdrivers_sd=[]):
  21. assert isinstance(jitdrivers_sd, list) # debugging
  22. self.cpu = cpu
  23. self.jitdrivers_sd = jitdrivers_sd
  24. self.jitcodes = {} # map {graph: jitcode}
  25. self.unfinished_graphs = [] # list of graphs with pending jitcodes
  26. self.callinfocollection = CallInfoCollection()
  27. if hasattr(cpu, 'rtyper'): # for tests
  28. self.rtyper = cpu.rtyper
  29. translator = self.rtyper.annotator.translator
  30. self.raise_analyzer = RaiseAnalyzer(translator)
  31. self.raise_analyzer_ignore_memoryerror = RaiseAnalyzer(translator)
  32. self.raise_analyzer_ignore_memoryerror.do_ignore_memory_error()
  33. self.readwrite_analyzer = ReadWriteAnalyzer(translator)
  34. self.virtualizable_analyzer = VirtualizableAnalyzer(translator)
  35. self.quasiimmut_analyzer = QuasiImmutAnalyzer(translator)
  36. self.randomeffects_analyzer = RandomEffectsAnalyzer(translator)
  37. self.collect_analyzer = CollectAnalyzer(translator)
  38. self.seen_rw = DependencyTracker(self.readwrite_analyzer)
  39. self.seen_gc = DependencyTracker(self.collect_analyzer)
  40. #
  41. for index, jd in enumerate(jitdrivers_sd):
  42. jd.index = index
  43. def find_all_graphs(self, policy):
  44. try:
  45. return self.candidate_graphs
  46. except AttributeError:
  47. pass
  48. is_candidate = policy.look_inside_graph
  49. assert len(self.jitdrivers_sd) > 0
  50. todo = [jd.portal_graph for jd in self.jitdrivers_sd]
  51. if hasattr(self, 'rtyper'):
  52. for oopspec_name, ll_args, ll_res in support.inline_calls_to:
  53. c_func, _ = support.builtin_func_for_spec(self.rtyper,
  54. oopspec_name,
  55. ll_args, ll_res)
  56. todo.append(c_func.value._obj.graph)
  57. candidate_graphs = set(todo)
  58. def callers():
  59. graph = top_graph
  60. print graph
  61. while graph in coming_from:
  62. graph = coming_from[graph]
  63. print '<-', graph
  64. coming_from = {}
  65. while todo:
  66. top_graph = todo.pop()
  67. for _, op in top_graph.iterblockops():
  68. if op.opname not in ("direct_call", "indirect_call"):
  69. continue
  70. kind = self.guess_call_kind(op, is_candidate)
  71. # use callers() to view the calling chain in pdb
  72. if kind != "regular":
  73. continue
  74. for graph in self.graphs_from(op, is_candidate):
  75. if graph in candidate_graphs:
  76. continue
  77. assert is_candidate(graph)
  78. todo.append(graph)
  79. candidate_graphs.add(graph)
  80. coming_from[graph] = top_graph
  81. self.candidate_graphs = candidate_graphs
  82. return candidate_graphs
  83. def graphs_from(self, op, is_candidate=None):
  84. if is_candidate is None:
  85. is_candidate = self.is_candidate
  86. if op.opname == 'direct_call':
  87. funcobj = op.args[0].value._obj
  88. graph = funcobj.graph
  89. if is_candidate(graph):
  90. return [graph] # common case: look inside this graph
  91. else:
  92. assert op.opname == 'indirect_call'
  93. graphs = op.args[-1].value
  94. if graphs is not None:
  95. result = []
  96. for graph in graphs:
  97. if is_candidate(graph):
  98. result.append(graph)
  99. if result:
  100. return result # common case: look inside these graphs,
  101. # and ignore the others if there are any
  102. # residual call case: we don't need to look into any graph
  103. return None
  104. def guess_call_kind(self, op, is_candidate=None):
  105. if op.opname == 'direct_call':
  106. funcptr = op.args[0].value
  107. if self.jitdriver_sd_from_portal_runner_ptr(funcptr) is not None:
  108. return 'recursive'
  109. funcobj = funcptr._obj
  110. assert (funcobj is not rposix._get_errno and
  111. funcobj is not rposix._set_errno), (
  112. "the JIT must never come close to _get_errno() or _set_errno();"
  113. " it should all be done at a lower level")
  114. if getattr(funcobj, 'graph', None) is None:
  115. return 'residual'
  116. targetgraph = funcobj.graph
  117. if hasattr(targetgraph, 'func'):
  118. # must never produce JitCode for a function with
  119. # _gctransformer_hint_close_stack_ set!
  120. if getattr(targetgraph.func,
  121. '_gctransformer_hint_close_stack_', False):
  122. return 'residual'
  123. if hasattr(targetgraph.func, 'oopspec'):
  124. return 'builtin'
  125. if self.graphs_from(op, is_candidate) is None:
  126. return 'residual'
  127. return 'regular'
  128. def is_candidate(self, graph):
  129. # used only after find_all_graphs()
  130. return graph in self.candidate_graphs
  131. def grab_initial_jitcodes(self):
  132. for jd in self.jitdrivers_sd:
  133. jd.mainjitcode = self.get_jitcode(jd.portal_graph)
  134. jd.mainjitcode.jitdriver_sd = jd
  135. def enum_pending_graphs(self):
  136. while self.unfinished_graphs:
  137. graph = self.unfinished_graphs.pop()
  138. yield graph, self.jitcodes[graph]
  139. def get_jitcode(self, graph, called_from=None):
  140. # 'called_from' is only one of the callers, used for debugging.
  141. try:
  142. return self.jitcodes[graph]
  143. except KeyError:
  144. # must never produce JitCode for a function with
  145. # _gctransformer_hint_close_stack_ set!
  146. if hasattr(graph, 'func') and getattr(graph.func,
  147. '_gctransformer_hint_close_stack_', False):
  148. raise AssertionError(
  149. '%s has _gctransformer_hint_close_stack_' % (graph,))
  150. #
  151. fnaddr, calldescr = self.get_jitcode_calldescr(graph)
  152. jitcode = JitCode(graph.name, fnaddr, calldescr,
  153. called_from=called_from)
  154. self.jitcodes[graph] = jitcode
  155. self.unfinished_graphs.append(graph)
  156. return jitcode
  157. def get_jitcode_calldescr(self, graph):
  158. """Return the calldescr that describes calls to the 'graph'.
  159. This returns a calldescr that is appropriate to attach to the
  160. jitcode corresponding to 'graph'. It has no extra effectinfo,
  161. because it is not needed there; it is only used by the blackhole
  162. interp to really do the call corresponding to 'inline_call' ops.
  163. """
  164. fnptr = getfunctionptr(graph)
  165. FUNC = lltype.typeOf(fnptr).TO
  166. fnaddr = llmemory.cast_ptr_to_adr(fnptr)
  167. NON_VOID_ARGS = [ARG for ARG in FUNC.ARGS if ARG is not lltype.Void]
  168. calldescr = self.cpu.calldescrof(FUNC, tuple(NON_VOID_ARGS),
  169. FUNC.RESULT, EffectInfo.MOST_GENERAL)
  170. return (fnaddr, calldescr)
  171. def getcalldescr(self, op, oopspecindex=EffectInfo.OS_NONE,
  172. extraeffect=None, extradescr=None):
  173. """Return the calldescr that describes all calls done by 'op'.
  174. This returns a calldescr that we can put in the corresponding
  175. call operation in the calling jitcode. It gets an effectinfo
  176. describing the effect of the call: which field types it may
  177. change, whether it can force virtualizables, whether it can
  178. raise, etc.
  179. """
  180. NON_VOID_ARGS = [x.concretetype for x in op.args[1:]
  181. if x.concretetype is not lltype.Void]
  182. RESULT = op.result.concretetype
  183. # check the number and type of arguments
  184. FUNC = op.args[0].concretetype.TO
  185. ARGS = FUNC.ARGS
  186. if NON_VOID_ARGS != [T for T in ARGS if T is not lltype.Void]:
  187. raise Exception(
  188. "in operation %r: caling a function with signature %r, "
  189. "but passing actual arguments (ignoring voids) of types %r"
  190. % (op, FUNC, NON_VOID_ARGS))
  191. if RESULT != FUNC.RESULT:
  192. raise Exception(
  193. "in operation %r: caling a function with signature %r, "
  194. "but the actual return type is %r" % (op, FUNC, RESULT))
  195. # ok
  196. # get the 'elidable' and 'loopinvariant' flags from the function object
  197. elidable = False
  198. loopinvariant = False
  199. call_release_gil_target = EffectInfo._NO_CALL_RELEASE_GIL_TARGET
  200. if op.opname == "direct_call":
  201. funcobj = op.args[0].value._obj
  202. assert getattr(funcobj, 'calling_conv', 'c') == 'c', (
  203. "%r: getcalldescr() with a non-default call ABI" % (op,))
  204. func = getattr(funcobj, '_callable', None)
  205. elidable = getattr(func, "_elidable_function_", False)
  206. loopinvariant = getattr(func, "_jit_loop_invariant_", False)
  207. if loopinvariant:
  208. assert not NON_VOID_ARGS, ("arguments not supported for "
  209. "loop-invariant function!")
  210. if getattr(func, "_call_aroundstate_target_", None):
  211. tgt_func, tgt_saveerr = func._call_aroundstate_target_
  212. tgt_func = llmemory.cast_ptr_to_adr(tgt_func)
  213. call_release_gil_target = (tgt_func, tgt_saveerr)
  214. elif op.opname == 'indirect_call':
  215. # check that we're not trying to call indirectly some
  216. # function with the special flags
  217. graphs = op.args[-1].value
  218. for graph in (graphs or ()):
  219. if not hasattr(graph, 'func'):
  220. continue
  221. error = None
  222. if hasattr(graph.func, '_elidable_function_'):
  223. error = '@jit.elidable'
  224. if hasattr(graph.func, '_jit_loop_invariant_'):
  225. error = '@jit.loop_invariant'
  226. if hasattr(graph.func, '_call_aroundstate_target_'):
  227. error = '_call_aroundstate_target_'
  228. if not error:
  229. continue
  230. raise Exception(
  231. "%r is an indirect call to a family of functions "
  232. "(or methods) that includes %r. However, the latter "
  233. "is marked %r. You need to use an indirection: replace "
  234. "it with a non-marked function/method which calls the "
  235. "marked function." % (op, graph, error))
  236. # build the extraeffect
  237. random_effects = self.randomeffects_analyzer.analyze(op)
  238. if random_effects:
  239. extraeffect = EffectInfo.EF_RANDOM_EFFECTS
  240. # random_effects implies can_invalidate
  241. can_invalidate = random_effects or self.quasiimmut_analyzer.analyze(op)
  242. if extraeffect is None:
  243. if self.virtualizable_analyzer.analyze(op):
  244. extraeffect = EffectInfo.EF_FORCES_VIRTUAL_OR_VIRTUALIZABLE
  245. elif loopinvariant:
  246. extraeffect = EffectInfo.EF_LOOPINVARIANT
  247. elif elidable:
  248. cr = self._canraise(op)
  249. if cr == "mem":
  250. extraeffect = EffectInfo.EF_ELIDABLE_OR_MEMORYERROR
  251. elif cr:
  252. extraeffect = EffectInfo.EF_ELIDABLE_CAN_RAISE
  253. else:
  254. extraeffect = EffectInfo.EF_ELIDABLE_CANNOT_RAISE
  255. elif self._canraise(op): # True or "mem"
  256. extraeffect = EffectInfo.EF_CAN_RAISE
  257. else:
  258. extraeffect = EffectInfo.EF_CANNOT_RAISE
  259. #
  260. # check that the result is really as expected
  261. if loopinvariant:
  262. if extraeffect != EffectInfo.EF_LOOPINVARIANT:
  263. raise Exception(
  264. "in operation %r: this calls a _jit_loop_invariant_ function,"
  265. " but this contradicts other sources (e.g. it can have random"
  266. " effects): EF=%s" % (op, extraeffect))
  267. if elidable:
  268. if extraeffect not in (EffectInfo.EF_ELIDABLE_CANNOT_RAISE,
  269. EffectInfo.EF_ELIDABLE_OR_MEMORYERROR,
  270. EffectInfo.EF_ELIDABLE_CAN_RAISE):
  271. raise Exception(
  272. "in operation %r: this calls an elidable function,"
  273. " but this contradicts other sources (e.g. it can have random"
  274. " effects): EF=%s" % (op, extraeffect))
  275. elif RESULT is lltype.Void:
  276. raise Exception(
  277. "in operation %r: this calls an elidable function "
  278. "but the function has no result" % (op, ))
  279. #
  280. effectinfo = effectinfo_from_writeanalyze(
  281. self.readwrite_analyzer.analyze(op, self.seen_rw), self.cpu,
  282. extraeffect, oopspecindex, can_invalidate, call_release_gil_target,
  283. extradescr, self.collect_analyzer.analyze(op, self.seen_gc),
  284. )
  285. #
  286. assert effectinfo is not None
  287. if elidable or loopinvariant:
  288. assert (effectinfo.extraeffect <
  289. EffectInfo.EF_FORCES_VIRTUAL_OR_VIRTUALIZABLE)
  290. # XXX this should also say assert not can_invalidate, but
  291. # it can't because our analyzer is not good enough for now
  292. # (and getexecutioncontext() can't really invalidate)
  293. #
  294. return self.cpu.calldescrof(FUNC, tuple(NON_VOID_ARGS), RESULT,
  295. effectinfo)
  296. def _canraise(self, op):
  297. """Returns True, False, or "mem" to mean 'only MemoryError'."""
  298. if op.opname == 'pseudo_call_cannot_raise':
  299. return False
  300. try:
  301. if self.raise_analyzer.can_raise(op):
  302. if self.raise_analyzer_ignore_memoryerror.can_raise(op):
  303. return True
  304. else:
  305. return "mem"
  306. else:
  307. return False
  308. except lltype.DelayedPointer:
  309. return True # if we need to look into the delayed ptr that is
  310. # the portal, then it's certainly going to raise
  311. def calldescr_canraise(self, calldescr):
  312. effectinfo = calldescr.get_extra_info()
  313. return effectinfo.check_can_raise()
  314. def jitdriver_sd_from_portal_graph(self, graph):
  315. for jd in self.jitdrivers_sd:
  316. if jd.portal_graph is graph:
  317. return jd
  318. return None
  319. def jitdriver_sd_from_portal_runner_ptr(self, funcptr):
  320. for jd in self.jitdrivers_sd:
  321. if funcptr is jd.portal_runner_ptr:
  322. return jd
  323. return None
  324. def jitdriver_sd_from_jitdriver(self, jitdriver):
  325. for jd in self.jitdrivers_sd:
  326. if jd.jitdriver is jitdriver:
  327. return jd
  328. return None
  329. def get_vinfo(self, VTYPEPTR):
  330. seen = set()
  331. for jd in self.jitdrivers_sd:
  332. if jd.virtualizable_info is not None:
  333. if jd.virtualizable_info.is_vtypeptr(VTYPEPTR):
  334. seen.add(jd.virtualizable_info)
  335. if seen:
  336. assert len(seen) == 1
  337. return seen.pop()
  338. else:
  339. return None
  340. def could_be_green_field(self, GTYPE, fieldname):
  341. GTYPE_fieldname = (GTYPE, fieldname)
  342. for jd in self.jitdrivers_sd:
  343. if jd.greenfield_info is not None:
  344. if GTYPE_fieldname in jd.greenfield_info.green_fields:
  345. return True
  346. return False