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

/rpython/jit/metainterp/warmspot.py

https://bitbucket.org/pypy/pypy/
Python | 1137 lines | 1028 code | 50 blank | 59 comment | 87 complexity | 674f3a00a7bcf000e9165813ff8bd260 MD5 | raw file
Possible License(s): AGPL-3.0, BSD-3-Clause, Apache-2.0
  1. import sys, py
  2. from rpython.tool.sourcetools import func_with_new_name
  3. from rpython.rtyper.lltypesystem import lltype, llmemory
  4. from rpython.rtyper.annlowlevel import (llhelper, MixLevelHelperAnnotator,
  5. cast_base_ptr_to_instance, hlstr, cast_instance_to_gcref)
  6. from rpython.rtyper.llannotation import lltype_to_annotation
  7. from rpython.annotator import model as annmodel
  8. from rpython.annotator.dictdef import DictDef
  9. from rpython.rtyper.llinterp import LLException
  10. from rpython.rtyper.test.test_llinterp import get_interpreter, clear_tcache
  11. from rpython.flowspace.model import SpaceOperation, Variable, Constant
  12. from rpython.flowspace.model import checkgraph, Link, copygraph
  13. from rpython.rlib.objectmodel import we_are_translated
  14. from rpython.rlib.unroll import unrolling_iterable
  15. from rpython.rlib.debug import fatalerror
  16. from rpython.rlib.rstackovf import StackOverflow
  17. from rpython.translator.backendopt import removenoops
  18. from rpython.translator.unsimplify import call_final_function
  19. from rpython.jit.metainterp import history, pyjitpl, gc, memmgr, jitexc
  20. from rpython.jit.metainterp.pyjitpl import MetaInterpStaticData
  21. from rpython.jit.metainterp.jitprof import Profiler, EmptyProfiler
  22. from rpython.jit.metainterp.jitdriver import JitDriverStaticData
  23. from rpython.jit.codewriter import support, codewriter
  24. from rpython.jit.codewriter.policy import JitPolicy
  25. from rpython.jit.codewriter.effectinfo import EffectInfo
  26. from rpython.jit.metainterp.optimizeopt import ALL_OPTS_NAMES
  27. from rpython.rlib.entrypoint import all_jit_entrypoints,\
  28. annotated_jit_entrypoints
  29. # ____________________________________________________________
  30. # Bootstrapping
  31. def apply_jit(translator, backend_name="auto", inline=False,
  32. vec=False, enable_opts=ALL_OPTS_NAMES, **kwds):
  33. if 'CPUClass' not in kwds:
  34. from rpython.jit.backend.detect_cpu import getcpuclass
  35. kwds['CPUClass'] = getcpuclass(backend_name)
  36. ProfilerClass = Profiler
  37. # Always use Profiler here, which should have a very low impact.
  38. # Otherwise you can try with ProfilerClass = EmptyProfiler.
  39. warmrunnerdesc = WarmRunnerDesc(translator,
  40. translate_support_code=True,
  41. listops=True,
  42. no_stats=True,
  43. ProfilerClass=ProfilerClass,
  44. **kwds)
  45. if len(warmrunnerdesc.jitdrivers_sd) == 1:
  46. jd = warmrunnerdesc.jitdrivers_sd[0]
  47. jd.jitdriver.is_recursive = True
  48. else:
  49. count_recursive = 0
  50. for jd in warmrunnerdesc.jitdrivers_sd:
  51. count_recursive += jd.jitdriver.is_recursive
  52. if count_recursive == 0:
  53. raise Exception("if you have more than one jitdriver, at least"
  54. " one of them has to be marked with is_recursive=True,"
  55. " none found")
  56. for jd in warmrunnerdesc.jitdrivers_sd:
  57. jd.warmstate.set_param_inlining(inline)
  58. jd.warmstate.set_param_vec(vec)
  59. jd.warmstate.set_param_enable_opts(enable_opts)
  60. warmrunnerdesc.finish()
  61. translator.warmrunnerdesc = warmrunnerdesc # for later debugging
  62. def ll_meta_interp(function, args, backendopt=False,
  63. listcomp=False, translationoptions={}, **kwds):
  64. if listcomp:
  65. extraconfigopts = {'translation.list_comprehension_operations': True}
  66. else:
  67. extraconfigopts = {}
  68. for key, value in translationoptions.items():
  69. extraconfigopts['translation.' + key] = value
  70. interp, graph = get_interpreter(function, args,
  71. backendopt=False, # will be done below
  72. **extraconfigopts)
  73. clear_tcache()
  74. return jittify_and_run(interp, graph, args, backendopt=backendopt, **kwds)
  75. def jittify_and_run(interp, graph, args, repeat=1, graph_and_interp_only=False,
  76. backendopt=False, trace_limit=sys.maxint, inline=False,
  77. loop_longevity=0, retrace_limit=5, function_threshold=4,
  78. disable_unrolling=sys.maxint,
  79. enable_opts=ALL_OPTS_NAMES, max_retrace_guards=15,
  80. max_unroll_recursion=7, vec=1, vec_all=0, vec_cost=0,
  81. vec_length=60, vec_ratio=2, vec_guard_ratio=3, **kwds):
  82. from rpython.config.config import ConfigError
  83. translator = interp.typer.annotator.translator
  84. try:
  85. translator.config.translation.gc = "boehm"
  86. except (ConfigError, TypeError):
  87. pass
  88. try:
  89. translator.config.translation.list_comprehension_operations = True
  90. except ConfigError:
  91. pass
  92. warmrunnerdesc = WarmRunnerDesc(translator, backendopt=backendopt, **kwds)
  93. for jd in warmrunnerdesc.jitdrivers_sd:
  94. jd.warmstate.set_param_threshold(3) # for tests
  95. jd.warmstate.set_param_function_threshold(function_threshold)
  96. jd.warmstate.set_param_trace_eagerness(2) # for tests
  97. jd.warmstate.set_param_trace_limit(trace_limit)
  98. jd.warmstate.set_param_inlining(inline)
  99. jd.warmstate.set_param_loop_longevity(loop_longevity)
  100. jd.warmstate.set_param_retrace_limit(retrace_limit)
  101. jd.warmstate.set_param_max_retrace_guards(max_retrace_guards)
  102. jd.warmstate.set_param_enable_opts(enable_opts)
  103. jd.warmstate.set_param_max_unroll_recursion(max_unroll_recursion)
  104. jd.warmstate.set_param_disable_unrolling(disable_unrolling)
  105. jd.warmstate.set_param_vec(vec)
  106. jd.warmstate.set_param_vec_all(vec_all)
  107. jd.warmstate.set_param_vec_cost(vec_cost)
  108. jd.warmstate.set_param_vec_length(vec_length)
  109. jd.warmstate.set_param_vec_ratio(vec_ratio)
  110. jd.warmstate.set_param_vec_guard_ratio(vec_guard_ratio)
  111. warmrunnerdesc.finish()
  112. if graph_and_interp_only:
  113. return interp, graph
  114. res = interp.eval_graph(graph, args)
  115. if not kwds.get('translate_support_code', False):
  116. warmrunnerdesc.metainterp_sd.jitlog.finish()
  117. warmrunnerdesc.metainterp_sd.profiler.finish()
  118. warmrunnerdesc.metainterp_sd.cpu.finish_once()
  119. print '~~~ return value:', repr(res)
  120. while repeat > 1:
  121. print '~' * 79
  122. res1 = interp.eval_graph(graph, args)
  123. if isinstance(res, int):
  124. assert res1 == res
  125. repeat -= 1
  126. return res
  127. def rpython_ll_meta_interp(function, args, backendopt=True, **kwds):
  128. return ll_meta_interp(function, args, backendopt=backendopt,
  129. translate_support_code=True, **kwds)
  130. def _find_jit_marker(graphs, marker_name, check_driver=True):
  131. results = []
  132. for graph in graphs:
  133. for block in graph.iterblocks():
  134. for i in range(len(block.operations)):
  135. op = block.operations[i]
  136. if (op.opname == 'jit_marker' and
  137. op.args[0].value == marker_name and
  138. (not check_driver or op.args[1].value is None or
  139. op.args[1].value.active)): # the jitdriver
  140. results.append((graph, block, i))
  141. return results
  142. def _find_jit_markers(graphs, marker_names):
  143. results = []
  144. for graph in graphs:
  145. for block in graph.iterblocks():
  146. for i in range(len(block.operations)):
  147. op = block.operations[i]
  148. if (op.opname == 'jit_marker' and
  149. op.args[0].value in marker_names):
  150. results.append((graph, block, i))
  151. return results
  152. def find_can_enter_jit(graphs):
  153. return _find_jit_marker(graphs, 'can_enter_jit')
  154. def find_loop_headers(graphs):
  155. return _find_jit_marker(graphs, 'loop_header')
  156. def find_jit_merge_points(graphs):
  157. results = _find_jit_marker(graphs, 'jit_merge_point')
  158. if not results:
  159. raise Exception("no jit_merge_point found!")
  160. seen = set([graph for graph, block, pos in results])
  161. assert len(seen) == len(results), (
  162. "found several jit_merge_points in the same graph")
  163. return results
  164. def find_access_helpers(graphs):
  165. return _find_jit_marker(graphs, 'access_helper', False)
  166. def locate_jit_merge_point(graph):
  167. [(graph, block, pos)] = find_jit_merge_points([graph])
  168. return block, pos, block.operations[pos]
  169. def find_set_param(graphs):
  170. return _find_jit_marker(graphs, 'set_param')
  171. def find_force_quasi_immutable(graphs):
  172. results = []
  173. for graph in graphs:
  174. for block in graph.iterblocks():
  175. for i in range(len(block.operations)):
  176. op = block.operations[i]
  177. if op.opname == 'jit_force_quasi_immutable':
  178. results.append((graph, block, i))
  179. return results
  180. def get_stats():
  181. return pyjitpl._warmrunnerdesc.stats
  182. def reset_stats():
  183. pyjitpl._warmrunnerdesc.stats.clear()
  184. def reset_jit():
  185. """Helper for some tests (see micronumpy/test/test_zjit.py)"""
  186. reset_stats()
  187. pyjitpl._warmrunnerdesc.memory_manager.alive_loops.clear()
  188. pyjitpl._warmrunnerdesc.jitcounter._clear_all()
  189. def get_translator():
  190. return pyjitpl._warmrunnerdesc.translator
  191. def debug_checks():
  192. stats = get_stats()
  193. stats.maybe_view()
  194. stats.check_consistency()
  195. # ____________________________________________________________
  196. class WarmRunnerDesc(object):
  197. def __init__(self, translator, policy=None, backendopt=True, CPUClass=None,
  198. ProfilerClass=EmptyProfiler, **kwds):
  199. pyjitpl._warmrunnerdesc = self # this is a global for debugging only!
  200. self.set_translator(translator)
  201. self.memory_manager = memmgr.MemoryManager()
  202. self.build_cpu(CPUClass, **kwds)
  203. self.inline_inlineable_portals()
  204. self.find_portals()
  205. self.codewriter = codewriter.CodeWriter(self.cpu, self.jitdrivers_sd)
  206. if policy is None:
  207. policy = JitPolicy()
  208. policy.set_supports_floats(self.cpu.supports_floats)
  209. policy.set_supports_longlong(self.cpu.supports_longlong)
  210. policy.set_supports_singlefloats(self.cpu.supports_singlefloats)
  211. graphs = self.codewriter.find_all_graphs(policy)
  212. policy.dump_unsafe_loops()
  213. self.check_access_directly_sanity(graphs)
  214. if backendopt:
  215. self.prejit_optimizations(policy, graphs)
  216. elif self.opt.listops:
  217. self.prejit_optimizations_minimal_inline(policy, graphs)
  218. self.build_meta_interp(ProfilerClass,
  219. translator.config.translation.jit_opencoder_model)
  220. self.make_args_specifications()
  221. #
  222. from rpython.jit.metainterp.virtualref import VirtualRefInfo
  223. vrefinfo = VirtualRefInfo(self)
  224. self.codewriter.setup_vrefinfo(vrefinfo)
  225. #
  226. from rpython.jit.metainterp import counter
  227. if self.cpu.translate_support_code:
  228. self.jitcounter = counter.JitCounter(translator=translator)
  229. else:
  230. self.jitcounter = counter.DeterministicJitCounter()
  231. #
  232. self.hooks = policy.jithookiface
  233. self.make_virtualizable_infos()
  234. self.make_driverhook_graphs()
  235. self.make_enter_functions()
  236. self.rewrite_jit_merge_points(policy)
  237. verbose = False # not self.cpu.translate_support_code
  238. self.rewrite_access_helpers()
  239. self.create_jit_entry_points()
  240. jitcodes = self.codewriter.make_jitcodes(verbose=verbose)
  241. self.metainterp_sd.jitcodes = jitcodes
  242. self.rewrite_can_enter_jits()
  243. self.rewrite_set_param_and_get_stats()
  244. self.rewrite_force_virtual(vrefinfo)
  245. self.rewrite_jitcell_accesses()
  246. self.rewrite_force_quasi_immutable()
  247. self.add_finish()
  248. self.metainterp_sd.finish_setup(self.codewriter)
  249. def finish(self):
  250. vinfos = set([jd.virtualizable_info for jd in self.jitdrivers_sd])
  251. for vinfo in vinfos:
  252. if vinfo is not None:
  253. vinfo.finish()
  254. self.metainterp_sd.finish_setup_descrs()
  255. if self.cpu.translate_support_code:
  256. self.annhelper.finish()
  257. def _freeze_(self):
  258. return True
  259. def set_translator(self, translator):
  260. self.translator = translator
  261. self.rtyper = translator.rtyper
  262. self.gcdescr = gc.get_description(translator.config)
  263. def inline_inlineable_portals(self):
  264. """
  265. Find all the graphs which have been decorated with @jitdriver.inline
  266. and inline them in the callers, making them JIT portals. Then, create
  267. a fresh copy of the jitdriver for each of those new portals, because
  268. they cannot share the same one. See
  269. test_ajit::test_inline_jit_merge_point
  270. """
  271. from rpython.translator.backendopt.inline import (
  272. inlinable_static_callers, auto_inlining)
  273. jmp_calls = {}
  274. def get_jmp_call(graph, _inline_jit_merge_point_):
  275. # there might be multiple calls to the @inlined function: the
  276. # first time we see it, we remove the call to the jit_merge_point
  277. # and we remember the corresponding op. Then, we create a new call
  278. # to it every time we need a new one (i.e., for each callsite
  279. # which becomes a new portal)
  280. try:
  281. op, jmp_graph = jmp_calls[graph]
  282. except KeyError:
  283. op, jmp_graph = fish_jmp_call(graph, _inline_jit_merge_point_)
  284. jmp_calls[graph] = op, jmp_graph
  285. #
  286. # clone the op
  287. newargs = op.args[:]
  288. newresult = Variable()
  289. newresult.concretetype = op.result.concretetype
  290. op = SpaceOperation(op.opname, newargs, newresult)
  291. return op, jmp_graph
  292. def fish_jmp_call(graph, _inline_jit_merge_point_):
  293. # graph is function which has been decorated with
  294. # @jitdriver.inline, so its very first op is a call to the
  295. # function which contains the actual jit_merge_point: fish it!
  296. jmp_block, op_jmp_call = next(callee.iterblockops())
  297. msg = ("The first operation of an _inline_jit_merge_point_ graph must be "
  298. "a direct_call to the function passed to @jitdriver.inline()")
  299. assert op_jmp_call.opname == 'direct_call', msg
  300. jmp_funcobj = op_jmp_call.args[0].value._obj
  301. assert jmp_funcobj._callable is _inline_jit_merge_point_, msg
  302. jmp_block.operations.remove(op_jmp_call)
  303. return op_jmp_call, jmp_funcobj.graph
  304. # find all the graphs which call an @inline_in_portal function
  305. callgraph = inlinable_static_callers(self.translator.graphs, store_calls=True)
  306. new_callgraph = []
  307. new_portals = set()
  308. inlined_jit_merge_points = set()
  309. for caller, block, op_call, callee in callgraph:
  310. func = getattr(callee, 'func', None)
  311. _inline_jit_merge_point_ = getattr(func, '_inline_jit_merge_point_', None)
  312. if _inline_jit_merge_point_:
  313. _inline_jit_merge_point_._always_inline_ = True
  314. inlined_jit_merge_points.add(_inline_jit_merge_point_)
  315. op_jmp_call, jmp_graph = get_jmp_call(callee, _inline_jit_merge_point_)
  316. #
  317. # now we move the op_jmp_call from callee to caller, just
  318. # before op_call. We assume that the args passed to
  319. # op_jmp_call are the very same which are received by callee
  320. # (i.e., the one passed to op_call)
  321. assert len(op_call.args) == len(op_jmp_call.args)
  322. op_jmp_call.args[1:] = op_call.args[1:]
  323. idx = block.operations.index(op_call)
  324. block.operations.insert(idx, op_jmp_call)
  325. #
  326. # finally, we signal that we want to inline op_jmp_call into
  327. # caller, so that finally the actuall call to
  328. # driver.jit_merge_point will be seen there
  329. new_callgraph.append((caller, jmp_graph))
  330. new_portals.add(caller)
  331. # inline them!
  332. inline_threshold = 0.1 # we rely on the _always_inline_ set above
  333. auto_inlining(self.translator, inline_threshold, new_callgraph)
  334. # clean up _always_inline_ = True, it can explode later
  335. for item in inlined_jit_merge_points:
  336. del item._always_inline_
  337. # make a fresh copy of the JitDriver in all newly created
  338. # jit_merge_points
  339. self.clone_inlined_jit_merge_points(new_portals)
  340. def clone_inlined_jit_merge_points(self, graphs):
  341. """
  342. Find all the jit_merge_points in the given graphs, and replace the
  343. original JitDriver with a fresh clone.
  344. """
  345. if not graphs:
  346. return
  347. for graph, block, pos in find_jit_merge_points(graphs):
  348. op = block.operations[pos]
  349. v_driver = op.args[1]
  350. driver = v_driver.value
  351. if not driver.inline_jit_merge_point:
  352. continue
  353. new_driver = driver.clone()
  354. c_new_driver = Constant(new_driver, v_driver.concretetype)
  355. op.args[1] = c_new_driver
  356. def find_portals(self):
  357. self.jitdrivers_sd = []
  358. graphs = self.translator.graphs
  359. for graph, block, pos in find_jit_merge_points(graphs):
  360. support.autodetect_jit_markers_redvars(graph)
  361. self.split_graph_and_record_jitdriver(graph, block, pos)
  362. #
  363. assert (len(set([jd.jitdriver for jd in self.jitdrivers_sd])) ==
  364. len(self.jitdrivers_sd)), \
  365. "there are multiple jit_merge_points with the same jitdriver"
  366. def split_graph_and_record_jitdriver(self, graph, block, pos):
  367. op = block.operations[pos]
  368. jd = JitDriverStaticData()
  369. jd._jit_merge_point_in = graph
  370. args = op.args[2:]
  371. s_binding = self.translator.annotator.binding
  372. jd._portal_args_s = [s_binding(v) for v in args]
  373. graph = copygraph(graph)
  374. [jmpp] = find_jit_merge_points([graph])
  375. graph.startblock = support.split_before_jit_merge_point(*jmpp)
  376. # XXX this is incredibly obscure, but this is sometiems necessary
  377. # so we don't explode in checkgraph. for reasons unknown this
  378. # is not contanied within simplify_graph
  379. removenoops.remove_same_as(graph)
  380. # a crash in the following checkgraph() means that you forgot
  381. # to list some variable in greens=[] or reds=[] in JitDriver,
  382. # or that a jit_merge_point() takes a constant as an argument.
  383. checkgraph(graph)
  384. for v in graph.getargs():
  385. assert isinstance(v, Variable)
  386. assert len(dict.fromkeys(graph.getargs())) == len(graph.getargs())
  387. self.translator.graphs.append(graph)
  388. jd.portal_graph = graph
  389. # it's a bit unbelievable to have a portal without func
  390. assert hasattr(graph, "func")
  391. graph.func._dont_inline_ = True
  392. graph.func._jit_unroll_safe_ = True
  393. jd.jitdriver = block.operations[pos].args[1].value
  394. jd.vec = jd.jitdriver.vec
  395. jd.portal_runner_ptr = "<not set so far>"
  396. jd.result_type = history.getkind(jd.portal_graph.getreturnvar()
  397. .concretetype)[0]
  398. self.jitdrivers_sd.append(jd)
  399. def check_access_directly_sanity(self, graphs):
  400. from rpython.translator.backendopt.inline import collect_called_graphs
  401. jit_graphs = set(graphs)
  402. for graph in collect_called_graphs(self.translator.entry_point_graph,
  403. self.translator):
  404. if graph in jit_graphs:
  405. continue
  406. assert not getattr(graph, 'access_directly', False)
  407. def prejit_optimizations(self, policy, graphs):
  408. from rpython.translator.backendopt.all import backend_optimizations
  409. backend_optimizations(self.translator,
  410. graphs=graphs,
  411. merge_if_blocks=True,
  412. constfold=True,
  413. remove_asserts=True,
  414. really_remove_asserts=True)
  415. def prejit_optimizations_minimal_inline(self, policy, graphs):
  416. from rpython.translator.backendopt.inline import auto_inline_graphs
  417. auto_inline_graphs(self.translator, graphs, 0.01)
  418. def build_cpu(self, CPUClass, translate_support_code=False,
  419. no_stats=False, supports_floats=True,
  420. supports_longlong=True, supports_singlefloats=True,
  421. **kwds):
  422. assert CPUClass is not None
  423. self.opt = history.Options(**kwds)
  424. if no_stats:
  425. stats = history.NoStats()
  426. else:
  427. stats = history.Stats(None)
  428. self.stats = stats
  429. if translate_support_code:
  430. self.annhelper = MixLevelHelperAnnotator(self.translator.rtyper)
  431. cpu = CPUClass(self.translator.rtyper, self.stats, self.opt,
  432. translate_support_code, gcdescr=self.gcdescr)
  433. if not supports_floats:
  434. cpu.supports_floats = False
  435. if not supports_longlong:
  436. cpu.supports_longlong = False
  437. if not supports_singlefloats:
  438. cpu.supports_singlefloats = False
  439. self.cpu = cpu
  440. def build_meta_interp(self, ProfilerClass, opencoder_model):
  441. from rpython.jit.metainterp.opencoder import Model, BigModel
  442. self.metainterp_sd = MetaInterpStaticData(self.cpu,
  443. self.opt,
  444. ProfilerClass=ProfilerClass,
  445. warmrunnerdesc=self)
  446. if opencoder_model == 'big':
  447. self.metainterp_sd.opencoder_model = BigModel
  448. else:
  449. self.metainterp_sd.opencoder_model = Model
  450. self.stats.metainterp_sd = self.metainterp_sd
  451. def make_virtualizable_infos(self):
  452. vinfos = {}
  453. for jd in self.jitdrivers_sd:
  454. #
  455. jd.greenfield_info = None
  456. for name in jd.jitdriver.greens:
  457. if '.' in name:
  458. from rpython.jit.metainterp.greenfield import GreenFieldInfo
  459. jd.greenfield_info = GreenFieldInfo(self.cpu, jd)
  460. break
  461. #
  462. if not jd.jitdriver.virtualizables:
  463. jd.virtualizable_info = None
  464. jd.index_of_virtualizable = -1
  465. continue
  466. else:
  467. assert jd.greenfield_info is None, "XXX not supported yet"
  468. #
  469. jitdriver = jd.jitdriver
  470. assert len(jitdriver.virtualizables) == 1 # for now
  471. [vname] = jitdriver.virtualizables
  472. # XXX skip the Voids here too
  473. jd.index_of_virtualizable = jitdriver.reds.index(vname)
  474. #
  475. index = jd.num_green_args + jd.index_of_virtualizable
  476. VTYPEPTR = jd._JIT_ENTER_FUNCTYPE.ARGS[index]
  477. if VTYPEPTR not in vinfos:
  478. from rpython.jit.metainterp.virtualizable import VirtualizableInfo
  479. vinfos[VTYPEPTR] = VirtualizableInfo(self, VTYPEPTR)
  480. jd.virtualizable_info = vinfos[VTYPEPTR]
  481. def make_enter_functions(self):
  482. for jd in self.jitdrivers_sd:
  483. self.make_enter_function(jd)
  484. def make_enter_function(self, jd):
  485. from rpython.jit.metainterp.warmstate import WarmEnterState
  486. state = WarmEnterState(self, jd)
  487. maybe_compile_and_run, EnterJitAssembler = state.make_entry_point()
  488. jd.warmstate = state
  489. def crash_in_jit(e):
  490. tb = not we_are_translated() and sys.exc_info()[2]
  491. try:
  492. raise e
  493. except jitexc.JitException:
  494. raise # go through
  495. except MemoryError:
  496. raise # go through
  497. except StackOverflow:
  498. raise # go through
  499. except Exception as e:
  500. if not we_are_translated():
  501. print "~~~ Crash in JIT!"
  502. print '~~~ %s: %s' % (e.__class__, e)
  503. if sys.stdout == sys.__stdout__:
  504. import pdb; pdb.post_mortem(tb)
  505. raise e.__class__, e, tb
  506. fatalerror('~~~ Crash in JIT! %s' % (e,))
  507. crash_in_jit._dont_inline_ = True
  508. def maybe_enter_jit(*args):
  509. try:
  510. maybe_compile_and_run(state.increment_threshold, *args)
  511. except Exception as e:
  512. crash_in_jit(e)
  513. maybe_enter_jit._always_inline_ = True
  514. jd._maybe_enter_jit_fn = maybe_enter_jit
  515. jd._maybe_compile_and_run_fn = maybe_compile_and_run
  516. jd._EnterJitAssembler = EnterJitAssembler
  517. def make_driverhook_graphs(self):
  518. #
  519. annhelper = MixLevelHelperAnnotator(self.translator.rtyper)
  520. for jd in self.jitdrivers_sd:
  521. jd._get_printable_location_ptr = self._make_hook_graph(jd,
  522. annhelper, jd.jitdriver.get_printable_location,
  523. annmodel.SomeString())
  524. jd._get_unique_id_ptr = self._make_hook_graph(jd,
  525. annhelper, jd.jitdriver.get_unique_id, annmodel.SomeInteger())
  526. jd._confirm_enter_jit_ptr = self._make_hook_graph(jd,
  527. annhelper, jd.jitdriver.confirm_enter_jit, annmodel.s_Bool,
  528. onlygreens=False)
  529. jd._can_never_inline_ptr = self._make_hook_graph(jd,
  530. annhelper, jd.jitdriver.can_never_inline, annmodel.s_Bool)
  531. jd._should_unroll_one_iteration_ptr = self._make_hook_graph(jd,
  532. annhelper, jd.jitdriver.should_unroll_one_iteration,
  533. annmodel.s_Bool)
  534. #
  535. items = []
  536. types = ()
  537. pos = ()
  538. if jd.jitdriver.get_location:
  539. assert hasattr(jd.jitdriver.get_location, '_loc_types'), """
  540. You must decorate your get_location function:
  541. from rpython.rlib.rjitlog import rjitlog as jl
  542. @jl.returns(jl.MP_FILENAME, jl.MP_XXX, ...)
  543. def get_loc(your, green, keys):
  544. name = "x.txt" # extract it from your green keys
  545. return (name, ...)
  546. """
  547. types = jd.jitdriver.get_location._loc_types
  548. del jd.jitdriver.get_location._loc_types
  549. #
  550. for _,type in types:
  551. if type == 's':
  552. items.append(annmodel.SomeString())
  553. elif type == 'i':
  554. items.append(annmodel.SomeInteger())
  555. else:
  556. raise NotImplementedError
  557. s_Tuple = annmodel.SomeTuple(items)
  558. jd._get_location_ptr = self._make_hook_graph(jd,
  559. annhelper, jd.jitdriver.get_location, s_Tuple)
  560. jd._get_loc_types = types
  561. annhelper.finish()
  562. def _make_hook_graph(self, jitdriver_sd, annhelper, func,
  563. s_result, s_first_arg=None, onlygreens=True):
  564. if func is None:
  565. return None
  566. #
  567. if not onlygreens:
  568. assert not jitdriver_sd.jitdriver.autoreds, (
  569. "reds='auto' is not compatible with JitDriver hooks such as "
  570. "confirm_enter_jit")
  571. extra_args_s = []
  572. if s_first_arg is not None:
  573. extra_args_s.append(s_first_arg)
  574. #
  575. args_s = jitdriver_sd._portal_args_s
  576. if onlygreens:
  577. args_s = args_s[:len(jitdriver_sd._green_args_spec)]
  578. graph = annhelper.getgraph(func, extra_args_s + args_s, s_result)
  579. funcptr = annhelper.graph2delayed(graph)
  580. return funcptr
  581. def make_args_specifications(self):
  582. for jd in self.jitdrivers_sd:
  583. self.make_args_specification(jd)
  584. def make_args_specification(self, jd):
  585. graph = jd._jit_merge_point_in
  586. _, _, op = locate_jit_merge_point(graph)
  587. greens_v, reds_v = support.decode_hp_hint_args(op)
  588. ALLARGS = [v.concretetype for v in (greens_v + reds_v)]
  589. jd._green_args_spec = [v.concretetype for v in greens_v]
  590. jd.red_args_types = [history.getkind(v.concretetype) for v in reds_v]
  591. jd.num_green_args = len(jd._green_args_spec)
  592. jd.num_red_args = len(jd.red_args_types)
  593. RESTYPE = graph.getreturnvar().concretetype
  594. (jd._JIT_ENTER_FUNCTYPE,
  595. jd._PTR_JIT_ENTER_FUNCTYPE) = self.cpu.ts.get_FuncType(ALLARGS, lltype.Void)
  596. (jd._PORTAL_FUNCTYPE,
  597. jd._PTR_PORTAL_FUNCTYPE) = self.cpu.ts.get_FuncType(ALLARGS, RESTYPE)
  598. #
  599. if jd.result_type == 'v':
  600. ASMRESTYPE = lltype.Void
  601. elif jd.result_type == history.INT:
  602. ASMRESTYPE = lltype.Signed
  603. elif jd.result_type == history.REF:
  604. ASMRESTYPE = llmemory.GCREF
  605. elif jd.result_type == history.FLOAT:
  606. ASMRESTYPE = lltype.Float
  607. else:
  608. assert False
  609. (_, jd._PTR_ASSEMBLER_HELPER_FUNCTYPE) = self.cpu.ts.get_FuncType(
  610. [llmemory.GCREF, llmemory.GCREF], ASMRESTYPE)
  611. def rewrite_jitcell_accesses(self):
  612. jitdrivers_by_name = {}
  613. for jd in self.jitdrivers_sd:
  614. name = jd.jitdriver.name
  615. if name != 'jitdriver':
  616. jitdrivers_by_name[name] = jd
  617. m = _find_jit_markers(self.translator.graphs,
  618. ('get_jitcell_at_key', 'trace_next_iteration',
  619. 'dont_trace_here', 'trace_next_iteration_hash'))
  620. accessors = {}
  621. def get_accessor(name, jitdriver_name, function, ARGS, green_arg_spec):
  622. a = accessors.get((name, jitdriver_name))
  623. if a:
  624. return a
  625. d = {'function': function,
  626. 'cast_instance_to_gcref': cast_instance_to_gcref,
  627. 'lltype': lltype}
  628. arg_spec = ", ".join([("arg%d" % i) for i in range(len(ARGS))])
  629. arg_converters = []
  630. for i, spec in enumerate(green_arg_spec):
  631. if isinstance(spec, lltype.Ptr):
  632. arg_converters.append("arg%d = lltype.cast_opaque_ptr(type%d, arg%d)" % (i, i, i))
  633. d['type%d' % i] = spec
  634. convert = ";".join(arg_converters)
  635. if name == 'get_jitcell_at_key':
  636. exec py.code.Source("""
  637. def accessor(%s):
  638. %s
  639. return cast_instance_to_gcref(function(%s))
  640. """ % (arg_spec, convert, arg_spec)).compile() in d
  641. FUNC = lltype.Ptr(lltype.FuncType(ARGS, llmemory.GCREF))
  642. elif name == "trace_next_iteration_hash":
  643. exec py.code.Source("""
  644. def accessor(arg0):
  645. function(arg0)
  646. """).compile() in d
  647. FUNC = lltype.Ptr(lltype.FuncType([lltype.Unsigned],
  648. lltype.Void))
  649. else:
  650. exec py.code.Source("""
  651. def accessor(%s):
  652. %s
  653. function(%s)
  654. """ % (arg_spec, convert, arg_spec)).compile() in d
  655. FUNC = lltype.Ptr(lltype.FuncType(ARGS, lltype.Void))
  656. func = d['accessor']
  657. ll_ptr = self.helper_func(FUNC, func)
  658. accessors[(name, jitdriver_name)] = ll_ptr
  659. return ll_ptr
  660. for graph, block, index in m:
  661. op = block.operations[index]
  662. jitdriver_name = op.args[1].value
  663. JitCell = jitdrivers_by_name[jitdriver_name].warmstate.JitCell
  664. ARGS = [x.concretetype for x in op.args[2:]]
  665. if op.args[0].value == 'get_jitcell_at_key':
  666. func = JitCell.get_jitcell
  667. elif op.args[0].value == 'dont_trace_here':
  668. func = JitCell.dont_trace_here
  669. elif op.args[0].value == 'trace_next_iteration_hash':
  670. func = JitCell.trace_next_iteration_hash
  671. else:
  672. func = JitCell._trace_next_iteration
  673. argspec = jitdrivers_by_name[jitdriver_name]._green_args_spec
  674. accessor = get_accessor(op.args[0].value,
  675. jitdriver_name, func,
  676. ARGS, argspec)
  677. v_result = op.result
  678. c_accessor = Constant(accessor, concretetype=lltype.Void)
  679. newop = SpaceOperation('direct_call', [c_accessor] + op.args[2:],
  680. v_result)
  681. block.operations[index] = newop
  682. def rewrite_can_enter_jits(self):
  683. sublists = {}
  684. for jd in self.jitdrivers_sd:
  685. sublists[jd.jitdriver] = jd, []
  686. jd.no_loop_header = True
  687. #
  688. loop_headers = find_loop_headers(self.translator.graphs)
  689. for graph, block, index in loop_headers:
  690. op = block.operations[index]
  691. jitdriver = op.args[1].value
  692. assert jitdriver in sublists, \
  693. "loop_header with no matching jit_merge_point"
  694. jd, sublist = sublists[jitdriver]
  695. jd.no_loop_header = False
  696. #
  697. can_enter_jits = find_can_enter_jit(self.translator.graphs)
  698. for graph, block, index in can_enter_jits:
  699. op = block.operations[index]
  700. jitdriver = op.args[1].value
  701. assert jitdriver in sublists, \
  702. "can_enter_jit with no matching jit_merge_point"
  703. assert not jitdriver.autoreds, (
  704. "can_enter_jit not supported with a jitdriver that "
  705. "has reds='auto'")
  706. jd, sublist = sublists[jitdriver]
  707. origportalgraph = jd._jit_merge_point_in
  708. if graph is not origportalgraph:
  709. sublist.append((graph, block, index))
  710. jd.no_loop_header = False
  711. else:
  712. pass # a 'can_enter_jit' before the 'jit-merge_point', but
  713. # originally in the same function: we ignore it here
  714. # see e.g. test_jitdriver.test_simple
  715. for jd in self.jitdrivers_sd:
  716. _, sublist = sublists[jd.jitdriver]
  717. self.rewrite_can_enter_jit(jd, sublist)
  718. def rewrite_can_enter_jit(self, jd, can_enter_jits):
  719. FUNCPTR = jd._PTR_JIT_ENTER_FUNCTYPE
  720. jit_enter_fnptr = self.helper_func(FUNCPTR, jd._maybe_enter_jit_fn)
  721. if len(can_enter_jits) == 0:
  722. # see test_warmspot.test_no_loop_at_all
  723. operations = jd.portal_graph.startblock.operations
  724. op1 = operations[0]
  725. assert (op1.opname == 'jit_marker' and
  726. op1.args[0].value == 'jit_merge_point')
  727. op0 = SpaceOperation(
  728. 'jit_marker',
  729. [Constant('can_enter_jit', lltype.Void)] + op1.args[1:],
  730. None)
  731. operations.insert(0, op0)
  732. can_enter_jits = [(jd.portal_graph, jd.portal_graph.startblock, 0)]
  733. for graph, block, index in can_enter_jits:
  734. if graph is jd._jit_merge_point_in:
  735. continue
  736. op = block.operations[index]
  737. greens_v, reds_v = support.decode_hp_hint_args(op)
  738. args_v = greens_v + reds_v
  739. vlist = [Constant(jit_enter_fnptr, FUNCPTR)] + args_v
  740. v_result = Variable()
  741. v_result.concretetype = lltype.Void
  742. newop = SpaceOperation('direct_call', vlist, v_result)
  743. block.operations[index] = newop
  744. def helper_func(self, FUNCPTR, func):
  745. if not self.cpu.translate_support_code:
  746. return llhelper(FUNCPTR, func)
  747. FUNC = FUNCPTR.TO
  748. args_s = [lltype_to_annotation(ARG) for ARG in FUNC.ARGS]
  749. s_result = lltype_to_annotation(FUNC.RESULT)
  750. graph = self.annhelper.getgraph(func, args_s, s_result)
  751. return self.annhelper.graph2delayed(graph, FUNC)
  752. def rewrite_access_helpers(self):
  753. ah = find_access_helpers(self.translator.graphs)
  754. for graph, block, index in ah:
  755. op = block.operations[index]
  756. self.rewrite_access_helper(op)
  757. def create_jit_entry_points(self):
  758. for func, args, result in all_jit_entrypoints:
  759. self.helper_func(lltype.Ptr(lltype.FuncType(args, result)), func)
  760. annotated_jit_entrypoints.append((func, None))
  761. def rewrite_access_helper(self, op):
  762. # make sure we make a copy of function so it no longer belongs
  763. # to extregistry
  764. func = op.args[1].value
  765. if func.func_name.startswith('stats_'):
  766. # get special treatment since we rewrite it to a call that accepts
  767. # jit driver
  768. assert len(op.args) >= 3, ("%r must have a first argument "
  769. "(which is None)" % (func,))
  770. func = func_with_new_name(func, func.func_name + '_compiled')
  771. def new_func(ignored, *args):
  772. return func(self, *args)
  773. ARGS = [lltype.Void] + [arg.concretetype for arg in op.args[3:]]
  774. else:
  775. ARGS = [arg.concretetype for arg in op.args[2:]]
  776. new_func = func_with_new_name(func, func.func_name + '_compiled')
  777. RESULT = op.result.concretetype
  778. FUNCPTR = lltype.Ptr(lltype.FuncType(ARGS, RESULT))
  779. ptr = self.helper_func(FUNCPTR, new_func)
  780. op.opname = 'direct_call'
  781. op.args = [Constant(ptr, FUNCPTR)] + op.args[2:]
  782. def rewrite_jit_merge_points(self, policy):
  783. for jd in self.jitdrivers_sd:
  784. self.rewrite_jit_merge_point(jd, policy)
  785. def rewrite_jit_merge_point(self, jd, policy):
  786. #
  787. # Mutate the original portal graph from this:
  788. #
  789. # def original_portal(..):
  790. # stuff
  791. # while 1:
  792. # jit_merge_point(*args)
  793. # more stuff
  794. #
  795. # to that:
  796. #
  797. # def original_portal(..):
  798. # stuff
  799. # return portal_runner(*args)
  800. #
  801. # def portal_runner(*args):
  802. # while 1:
  803. # try:
  804. # return portal(*args)
  805. # except JitException, e:
  806. # return handle_jitexception(e)
  807. #
  808. # def portal(*args):
  809. # while 1:
  810. # more stuff
  811. #
  812. origportalgraph = jd._jit_merge_point_in
  813. portalgraph = jd.portal_graph
  814. PORTALFUNC = jd._PORTAL_FUNCTYPE
  815. # ____________________________________________________________
  816. # Prepare the portal_runner() helper
  817. #
  818. from rpython.jit.metainterp.warmstate import specialize_value
  819. from rpython.jit.metainterp.warmstate import unspecialize_value
  820. portal_ptr = self.cpu.ts.functionptr(PORTALFUNC, 'portal',
  821. graph=portalgraph)
  822. jd._portal_ptr = portal_ptr
  823. #
  824. portalfunc_ARGS = []
  825. nums = {}
  826. for i, ARG in enumerate(PORTALFUNC.ARGS):
  827. kind = history.getkind(ARG)
  828. assert kind != 'void'
  829. if i < len(jd.jitdriver.greens):
  830. color = 'green'
  831. else:
  832. color = 'red'
  833. attrname = '%s_%s' % (color, kind)
  834. count = nums.get(attrname, 0)
  835. nums[attrname] = count + 1
  836. portalfunc_ARGS.append((ARG, attrname, count))
  837. portalfunc_ARGS = unrolling_iterable(portalfunc_ARGS)
  838. #
  839. rtyper = self.translator.rtyper
  840. RESULT = PORTALFUNC.RESULT
  841. result_kind = history.getkind(RESULT)
  842. assert result_kind.startswith(jd.result_type)
  843. ts = self.cpu.ts
  844. state = jd.warmstate
  845. maybe_compile_and_run = jd._maybe_compile_and_run_fn
  846. EnterJitAssembler = jd._EnterJitAssembler
  847. def ll_portal_runner(*args):
  848. try:
  849. # maybe enter from the function's start.
  850. maybe_compile_and_run(
  851. state.increment_function_threshold, *args)
  852. #
  853. # then run the normal portal function, i.e. the
  854. # interpreter's main loop. It might enter the jit
  855. # via maybe_enter_jit(), which typically ends with
  856. # handle_fail() being called, which raises on the
  857. # following exceptions --- catched here, because we
  858. # want to interrupt the whole interpreter loop.
  859. return support.maybe_on_top_of_llinterp(rtyper,
  860. portal_ptr)(*args)
  861. except jitexc.JitException as e:
  862. result = handle_jitexception(e)
  863. if result_kind != 'void':
  864. result = specialize_value(RESULT, result)
  865. return result
  866. def handle_jitexception(e):
  867. # XXX there are too many exceptions all around...
  868. while True:
  869. if isinstance(e, EnterJitAssembler):
  870. try:
  871. return e.execute()
  872. except jitexc.JitException as e:
  873. continue
  874. #
  875. if isinstance(e, jitexc.ContinueRunningNormally):
  876. args = ()
  877. for ARGTYPE, attrname, count in portalfunc_ARGS:
  878. x = getattr(e, attrname)[count]
  879. x = specialize_value(ARGTYPE, x)
  880. args = args + (x,)
  881. try:
  882. result = support.maybe_on_top_of_llinterp(rtyper,
  883. portal_ptr)(*args)
  884. except jitexc.JitException as e:
  885. continue
  886. if result_kind != 'void':
  887. result = unspecialize_value(result)
  888. return result
  889. #
  890. if result_kind == 'void':
  891. if isinstance(e, jitexc.DoneWithThisFrameVoid):
  892. return None
  893. if result_kind == 'int':
  894. if isinstance(e, jitexc.DoneWithThisFrameInt):
  895. return e.result
  896. if result_kind == 'ref':
  897. if isinstance(e, jitexc.DoneWithThisFrameRef):
  898. return e.result
  899. if result_kind == 'float':
  900. if isinstance(e, jitexc.DoneWithThisFrameFloat):
  901. return e.result
  902. #
  903. if isinstance(e, jitexc.ExitFrameWithExceptionRef):
  904. value = ts.cast_to_baseclass(e.value)
  905. if not we_are_translated():
  906. raise LLException(ts.get_typeptr(value), value)
  907. else:
  908. value = cast_base_ptr_to_instance(Exception, value)
  909. assert value is not None
  910. raise value
  911. #
  912. raise AssertionError("all cases should have been handled")
  913. jd._ll_portal_runner = ll_portal_runner # for debugging
  914. jd.portal_runner_ptr = self.helper_func(jd._PTR_PORTAL_FUNCTYPE,
  915. ll_portal_runner)
  916. jd.portal_runner_adr = llmemory.cast_ptr_to_adr(jd.portal_runner_ptr)
  917. jd.portal_calldescr = self.cpu.calldescrof(
  918. jd._PTR_PORTAL_FUNCTYPE.TO,
  919. jd._PTR_PORTAL_FUNCTYPE.TO.ARGS,
  920. jd._PTR_PORTAL_FUNCTYPE.TO.RESULT,
  921. EffectInfo.MOST_GENERAL)
  922. vinfo = jd.virtualizable_info
  923. def assembler_call_helper(deadframe, virtualizableref):
  924. fail_descr = self.cpu.get_latest_descr(deadframe)
  925. try:
  926. fail_descr.handle_fail(deadframe, self.metainterp_sd, jd)
  927. except jitexc.JitException as e:
  928. return handle_jitexception(e)
  929. else:
  930. assert 0, "should have raised"
  931. jd._assembler_call_helper = assembler_call_helper # for debugging
  932. jd._assembler_helper_ptr = self.helper_func(
  933. jd._PTR_ASSEMBLER_HELPER_FUNCTYPE,
  934. assembler_call_helper)
  935. jd.assembler_helper_adr = llmemory.cast_ptr_to_adr(
  936. jd._assembler_helper_ptr)
  937. if vinfo is not None:
  938. jd.vable_token_descr = vinfo.vable_token_descr
  939. def handle_jitexception_from_blackhole(bhcaller, e):
  940. result = handle_jitexception(e)
  941. if result_kind == 'void':
  942. pass
  943. elif result_kind == 'int':
  944. bhcaller._setup_return_value_i(result)
  945. elif result_kind == 'ref':
  946. bhcaller._setup_return_value_r(result)
  947. elif result_kind == 'float':
  948. bhcaller._setup_return_value_f(result)
  949. else:
  950. assert False
  951. jd.handle_jitexc_from_bh = handle_jitexception_from_blackhole
  952. # ____________________________________________________________
  953. # Now mutate origportalgraph to end with a call to portal_runner_ptr
  954. #
  955. origblock, origindex, op = locate_jit_merge_point(origportalgraph)
  956. assert op.opname == 'jit_marker'
  957. assert op.args[0].value == 'jit_merge_point'
  958. greens_v, reds_v = support.decode_hp_hint_args(op)
  959. vlist = [Constant(jd.portal_runner_ptr, jd._PTR_PORTAL_FUNCTYPE)]
  960. vlist += greens_v
  961. vlist += reds_v
  962. v_result = Variable()
  963. v_result.concretetype = PORTALFUNC.RESULT
  964. newop = SpaceOperation('direct_call', vlist, v_result)
  965. del origblock.operations[origindex:]
  966. origblock.operations.append(newop)
  967. origblock.exitswitch = None
  968. origblock.recloseblock(Link([v_result], origportalgraph.returnblock))
  969. # the origportal now can raise (even if it did not raise before),
  970. # which means that we cannot inline it anywhere any more, but that's
  971. # fine since any forced inlining has been done before
  972. #
  973. checkgraph(origportalgraph)
  974. def add_finish(self):
  975. def finish():
  976. if self.metainterp_sd.profiler.initialized:
  977. self.metainterp_sd.profiler.finish()
  978. self.metainterp_sd.cpu.finish_once()
  979. if self.cpu.translate_support_code:
  980. call_final_function(self.translator, finish,
  981. annhelper=self.annhelper)
  982. def rewrite_set_param_and_get_stats(self):
  983. from rpython.rtyper.lltypesystem.rstr import STR
  984. closures = {}
  985. graphs = self.translator.graphs
  986. _, PTR_SET_PARAM_FUNCTYPE = self.cpu.ts.get_FuncType([lltype.Signed],
  987. lltype.Void)
  988. _, PTR_SET_PARAM_STR_FUNCTYPE = self.cpu.ts.get_FuncType(
  989. [lltype.Ptr(STR)], lltype.Void)
  990. def make_closure(jd, fullfuncname, is_string):
  991. if jd is None:
  992. def closure(i):
  993. if is_string:
  994. i = hlstr(i)
  995. for jd in self.jitdrivers_sd:
  996. getattr(jd.warmstate, fullfuncname)(i)
  997. else:
  998. state = jd.warmstate
  999. def closure(i):
  1000. if is_string:
  1001. i = hlstr(i)
  1002. getattr(state, fullfuncname)(i)
  1003. if is_string:
  1004. TP = PTR_SET_PARAM_STR_FUNCTYPE
  1005. else:
  1006. TP = PTR_SET_PARAM_FUNCTYPE
  1007. funcptr = self.helper_func(TP, closure)
  1008. return Constant(funcptr, TP)
  1009. #
  1010. for graph, block, i in find_set_param(graphs):
  1011. op = block.operations[i]
  1012. if op.args[1].value is not None:
  1013. for jd in self.jitdrivers_sd:
  1014. if jd.jitdriver is op.args[1].value:
  1015. break
  1016. else:
  1017. assert 0, "jitdriver of set_param() not found"
  1018. else:
  1019. jd = None
  1020. funcname = op.args[2].value
  1021. key = jd, funcname
  1022. if key not in closures:
  1023. closures[key] = make_closure(jd, 'set_param_' + funcname,
  1024. funcname == 'enable_opts')
  1025. op.opname = 'direct_call'
  1026. op.args[:3] = [closures[key]]
  1027. def rewrite_force_virtual(self, vrefinfo):
  1028. all_graphs = self.translator.graphs
  1029. vrefinfo.replace_force_virtual_with_call(all_graphs)
  1030. def replace_force_quasiimmut_with_direct_call(self, op):
  1031. ARG = op.args[0].concretetype
  1032. mutatefieldname = op.args[1].value
  1033. key = (ARG, mutatefieldname)
  1034. if key in self._cache_force_quasiimmed_funcs:
  1035. cptr = self._cache_force_quasiimmed_funcs[key]
  1036. else:
  1037. from rpython.jit.metainterp import quasiimmut
  1038. func = quasiimmut.make_invalidation_function(ARG, mutatefieldname)
  1039. FUNC = lltype.Ptr(lltype.FuncType([ARG], lltype.Void))
  1040. llptr = self.helper_func(FUNC, func)
  1041. cptr = Constant(llptr, FUNC)
  1042. self._cache_force_quasiimmed_funcs[key] = cptr
  1043. op.opname = 'direct_call'
  1044. op.args = [cptr, op.args[0]]
  1045. def rewrite_force_quasi_immutable(self):
  1046. self._cache_force_quasiimmed_funcs = {}
  1047. graphs = self.translator.graphs
  1048. for graph, block, i in find_force_quasi_immutable(graphs):
  1049. self.replace_force_quasiimmut_with_direct_call(block.operations[i])