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

/rpython/jit/metainterp/compile.py

https://bitbucket.org/kcr/pypy
Python | 922 lines | 788 code | 64 blank | 70 comment | 90 complexity | da8146d67e018a4532f1b7459e1e1e7b MD5 | raw file
Possible License(s): Apache-2.0
  1. import weakref
  2. from rpython.rtyper.lltypesystem import lltype
  3. from rpython.rtyper.annlowlevel import cast_instance_to_gcref
  4. from rpython.rlib.objectmodel import we_are_translated
  5. from rpython.rlib.debug import debug_start, debug_stop, debug_print
  6. from rpython.rlib import rstack
  7. from rpython.rlib.jit import JitDebugInfo, Counters, dont_look_inside
  8. from rpython.conftest import option
  9. from rpython.tool.sourcetools import func_with_new_name
  10. from rpython.jit.metainterp.resoperation import ResOperation, rop, get_deep_immutable_oplist
  11. from rpython.jit.metainterp.history import TreeLoop, Box, History, JitCellToken, TargetToken
  12. from rpython.jit.metainterp.history import AbstractFailDescr, BoxInt
  13. from rpython.jit.metainterp.history import BoxPtr, BoxObj, BoxFloat, Const, ConstInt
  14. from rpython.jit.metainterp import history, resume
  15. from rpython.jit.metainterp.typesystem import llhelper, oohelper
  16. from rpython.jit.metainterp.optimize import InvalidLoop
  17. from rpython.jit.metainterp.inliner import Inliner
  18. from rpython.jit.metainterp.resume import NUMBERING, PENDINGFIELDSP
  19. from rpython.jit.codewriter import heaptracker, longlong
  20. def giveup():
  21. from rpython.jit.metainterp.pyjitpl import SwitchToBlackhole
  22. raise SwitchToBlackhole(Counters.ABORT_BRIDGE)
  23. def show_procedures(metainterp_sd, procedure=None, error=None):
  24. # debugging
  25. if option and (option.view or option.viewloops):
  26. if error:
  27. errmsg = error.__class__.__name__
  28. if str(error):
  29. errmsg += ': ' + str(error)
  30. else:
  31. errmsg = None
  32. if procedure is None:
  33. extraprocedures = []
  34. else:
  35. extraprocedures = [procedure]
  36. metainterp_sd.stats.view(errmsg=errmsg,
  37. extraprocedures=extraprocedures,
  38. metainterp_sd=metainterp_sd)
  39. def create_empty_loop(metainterp, name_prefix=''):
  40. name = metainterp.staticdata.stats.name_for_new_loop()
  41. loop = TreeLoop(name_prefix + name)
  42. loop.call_pure_results = metainterp.call_pure_results
  43. return loop
  44. def make_jitcell_token(jitdriver_sd):
  45. jitcell_token = JitCellToken()
  46. jitcell_token.outermost_jitdriver_sd = jitdriver_sd
  47. return jitcell_token
  48. def record_loop_or_bridge(metainterp_sd, loop):
  49. """Do post-backend recordings and cleanups on 'loop'.
  50. """
  51. # get the original jitcell token corresponding to jitcell form which
  52. # this trace starts
  53. original_jitcell_token = loop.original_jitcell_token
  54. assert original_jitcell_token is not None
  55. if metainterp_sd.warmrunnerdesc is not None: # for tests
  56. assert original_jitcell_token.generation > 0 # has been registered with memmgr
  57. wref = weakref.ref(original_jitcell_token)
  58. for op in loop.operations:
  59. descr = op.getdescr()
  60. if isinstance(descr, ResumeDescr):
  61. descr.wref_original_loop_token = wref # stick it there
  62. n = descr.index
  63. if n >= 0: # we also record the resumedescr number
  64. original_jitcell_token.compiled_loop_token.record_faildescr_index(n)
  65. elif isinstance(descr, JitCellToken):
  66. # for a CALL_ASSEMBLER: record it as a potential jump.
  67. if descr is not original_jitcell_token:
  68. original_jitcell_token.record_jump_to(descr)
  69. descr.exported_state = None
  70. op.cleardescr() # clear reference, mostly for tests
  71. elif isinstance(descr, TargetToken):
  72. # for a JUMP: record it as a potential jump.
  73. # (the following test is not enough to prevent more complicated
  74. # cases of cycles, but at least it helps in simple tests of
  75. # test_memgr.py)
  76. if descr.original_jitcell_token is not original_jitcell_token:
  77. assert descr.original_jitcell_token is not None
  78. original_jitcell_token.record_jump_to(descr.original_jitcell_token)
  79. # exported_state is clear by optimizeopt when the short preamble is
  80. # constrcucted. if that did not happen the label should not show up
  81. # in a trace that will be used
  82. assert descr.exported_state is None
  83. if not we_are_translated():
  84. op._descr_wref = weakref.ref(op._descr)
  85. op.cleardescr() # clear reference to prevent the history.Stats
  86. # from keeping the loop alive during tests
  87. # record this looptoken on the QuasiImmut used in the code
  88. if loop.quasi_immutable_deps is not None:
  89. for qmut in loop.quasi_immutable_deps:
  90. qmut.register_loop_token(wref)
  91. # XXX maybe we should clear the dictionary here
  92. # mostly for tests: make sure we don't keep a reference to the LoopToken
  93. loop.original_jitcell_token = None
  94. if not we_are_translated():
  95. loop._looptoken_number = original_jitcell_token.number
  96. # ____________________________________________________________
  97. def compile_loop(metainterp, greenkey, start,
  98. inputargs, jumpargs,
  99. resume_at_jump_descr, full_preamble_needed=True,
  100. try_disabling_unroll=False):
  101. """Try to compile a new procedure by closing the current history back
  102. to the first operation.
  103. """
  104. from rpython.jit.metainterp.optimizeopt import optimize_trace
  105. metainterp_sd = metainterp.staticdata
  106. jitdriver_sd = metainterp.jitdriver_sd
  107. history = metainterp.history
  108. enable_opts = jitdriver_sd.warmstate.enable_opts
  109. if try_disabling_unroll:
  110. if 'unroll' not in enable_opts:
  111. return None
  112. enable_opts = enable_opts.copy()
  113. del enable_opts['unroll']
  114. jitcell_token = make_jitcell_token(jitdriver_sd)
  115. part = create_empty_loop(metainterp)
  116. part.inputargs = inputargs[:]
  117. h_ops = history.operations
  118. part.resume_at_jump_descr = resume_at_jump_descr
  119. part.operations = [ResOperation(rop.LABEL, inputargs, None, descr=TargetToken(jitcell_token))] + \
  120. [h_ops[i].clone() for i in range(start, len(h_ops))] + \
  121. [ResOperation(rop.LABEL, jumpargs, None, descr=jitcell_token)]
  122. try:
  123. optimize_trace(metainterp_sd, part, enable_opts)
  124. except InvalidLoop:
  125. return None
  126. target_token = part.operations[0].getdescr()
  127. assert isinstance(target_token, TargetToken)
  128. all_target_tokens = [target_token]
  129. loop = create_empty_loop(metainterp)
  130. loop.inputargs = part.inputargs
  131. loop.operations = part.operations
  132. loop.quasi_immutable_deps = {}
  133. if part.quasi_immutable_deps:
  134. loop.quasi_immutable_deps.update(part.quasi_immutable_deps)
  135. while part.operations[-1].getopnum() == rop.LABEL:
  136. inliner = Inliner(inputargs, jumpargs)
  137. part.quasi_immutable_deps = None
  138. part.operations = [part.operations[-1]] + \
  139. [inliner.inline_op(h_ops[i]) for i in range(start, len(h_ops))] + \
  140. [ResOperation(rop.JUMP, [inliner.inline_arg(a) for a in jumpargs],
  141. None, descr=jitcell_token)]
  142. target_token = part.operations[0].getdescr()
  143. assert isinstance(target_token, TargetToken)
  144. all_target_tokens.append(target_token)
  145. inputargs = jumpargs
  146. jumpargs = part.operations[-1].getarglist()
  147. try:
  148. optimize_trace(metainterp_sd, part, enable_opts)
  149. except InvalidLoop:
  150. return None
  151. loop.operations = loop.operations[:-1] + part.operations
  152. if part.quasi_immutable_deps:
  153. loop.quasi_immutable_deps.update(part.quasi_immutable_deps)
  154. if not loop.quasi_immutable_deps:
  155. loop.quasi_immutable_deps = None
  156. for box in loop.inputargs:
  157. assert isinstance(box, Box)
  158. loop.original_jitcell_token = jitcell_token
  159. for label in all_target_tokens:
  160. assert isinstance(label, TargetToken)
  161. if label.virtual_state and label.short_preamble:
  162. metainterp_sd.logger_ops.log_short_preamble([], label.short_preamble)
  163. jitcell_token.target_tokens = all_target_tokens
  164. propagate_original_jitcell_token(loop)
  165. send_loop_to_backend(greenkey, jitdriver_sd, metainterp_sd, loop, "loop")
  166. record_loop_or_bridge(metainterp_sd, loop)
  167. return all_target_tokens[0]
  168. def compile_retrace(metainterp, greenkey, start,
  169. inputargs, jumpargs,
  170. resume_at_jump_descr, partial_trace, resumekey):
  171. """Try to compile a new procedure by closing the current history back
  172. to the first operation.
  173. """
  174. from rpython.jit.metainterp.optimizeopt import optimize_trace
  175. history = metainterp.history
  176. metainterp_sd = metainterp.staticdata
  177. jitdriver_sd = metainterp.jitdriver_sd
  178. loop_jitcell_token = metainterp.get_procedure_token(greenkey)
  179. assert loop_jitcell_token
  180. assert partial_trace.operations[-1].getopnum() == rop.LABEL
  181. part = create_empty_loop(metainterp)
  182. part.inputargs = inputargs[:]
  183. part.resume_at_jump_descr = resume_at_jump_descr
  184. h_ops = history.operations
  185. part.operations = [partial_trace.operations[-1]] + \
  186. [h_ops[i].clone() for i in range(start, len(h_ops))] + \
  187. [ResOperation(rop.JUMP, jumpargs, None, descr=loop_jitcell_token)]
  188. label = part.operations[0]
  189. orignial_label = label.clone()
  190. assert label.getopnum() == rop.LABEL
  191. try:
  192. optimize_trace(metainterp_sd, part, jitdriver_sd.warmstate.enable_opts)
  193. except InvalidLoop:
  194. # Fall back on jumping to preamble
  195. target_token = label.getdescr()
  196. assert isinstance(target_token, TargetToken)
  197. assert target_token.exported_state
  198. part.operations = [orignial_label] + \
  199. [ResOperation(rop.JUMP, inputargs[:],
  200. None, descr=loop_jitcell_token)]
  201. try:
  202. optimize_trace(metainterp_sd, part, jitdriver_sd.warmstate.enable_opts,
  203. inline_short_preamble=False)
  204. except InvalidLoop:
  205. return None
  206. assert part.operations[-1].getopnum() != rop.LABEL
  207. target_token = label.getdescr()
  208. assert isinstance(target_token, TargetToken)
  209. assert loop_jitcell_token.target_tokens
  210. loop_jitcell_token.target_tokens.append(target_token)
  211. if target_token.short_preamble:
  212. metainterp_sd.logger_ops.log_short_preamble([], target_token.short_preamble)
  213. loop = partial_trace
  214. loop.operations = loop.operations[:-1] + part.operations
  215. quasi_immutable_deps = {}
  216. if loop.quasi_immutable_deps:
  217. quasi_immutable_deps.update(loop.quasi_immutable_deps)
  218. if part.quasi_immutable_deps:
  219. quasi_immutable_deps.update(part.quasi_immutable_deps)
  220. if quasi_immutable_deps:
  221. loop.quasi_immutable_deps = quasi_immutable_deps
  222. for box in loop.inputargs:
  223. assert isinstance(box, Box)
  224. target_token = loop.operations[-1].getdescr()
  225. resumekey.compile_and_attach(metainterp, loop)
  226. target_token = label.getdescr()
  227. assert isinstance(target_token, TargetToken)
  228. record_loop_or_bridge(metainterp_sd, loop)
  229. return target_token
  230. def patch_new_loop_to_load_virtualizable_fields(loop, jitdriver_sd):
  231. vinfo = jitdriver_sd.virtualizable_info
  232. extra_ops = []
  233. inputargs = loop.inputargs
  234. vable_box = inputargs[jitdriver_sd.index_of_virtualizable]
  235. i = jitdriver_sd.num_red_args
  236. loop.inputargs = inputargs[:i]
  237. for descr in vinfo.static_field_descrs:
  238. assert i < len(inputargs)
  239. box = inputargs[i]
  240. extra_ops.append(
  241. ResOperation(rop.GETFIELD_GC, [vable_box], box, descr))
  242. i += 1
  243. arrayindex = 0
  244. for descr in vinfo.array_field_descrs:
  245. vable = vable_box.getref_base()
  246. arraylen = vinfo.get_array_length(vable, arrayindex)
  247. arraybox = BoxPtr()
  248. extra_ops.append(
  249. ResOperation(rop.GETFIELD_GC, [vable_box], arraybox, descr))
  250. arraydescr = vinfo.array_descrs[arrayindex]
  251. assert i + arraylen <= len(inputargs)
  252. for index in range(arraylen):
  253. box = inputargs[i]
  254. extra_ops.append(
  255. ResOperation(rop.GETARRAYITEM_GC,
  256. [arraybox, ConstInt(index)],
  257. box, descr=arraydescr))
  258. i += 1
  259. arrayindex += 1
  260. assert i == len(inputargs)
  261. loop.operations = extra_ops + loop.operations
  262. def propagate_original_jitcell_token(trace):
  263. for op in trace.operations:
  264. if op.getopnum() == rop.LABEL:
  265. token = op.getdescr()
  266. assert isinstance(token, TargetToken)
  267. assert token.original_jitcell_token is None
  268. token.original_jitcell_token = trace.original_jitcell_token
  269. def do_compile_loop(metainterp_sd, inputargs, operations, looptoken,
  270. log=True, name=''):
  271. metainterp_sd.logger_ops.log_loop(inputargs, operations, -2,
  272. 'compiling', name=name)
  273. return metainterp_sd.cpu.compile_loop(inputargs, operations, looptoken,
  274. log=log, name=name)
  275. def do_compile_bridge(metainterp_sd, faildescr, inputargs, operations,
  276. original_loop_token, log=True):
  277. metainterp_sd.logger_ops.log_bridge(inputargs, operations, "compiling")
  278. assert isinstance(faildescr, AbstractFailDescr)
  279. return metainterp_sd.cpu.compile_bridge(faildescr, inputargs, operations,
  280. original_loop_token, log=log)
  281. def send_loop_to_backend(greenkey, jitdriver_sd, metainterp_sd, loop, type):
  282. vinfo = jitdriver_sd.virtualizable_info
  283. if vinfo is not None:
  284. patch_new_loop_to_load_virtualizable_fields(loop, jitdriver_sd)
  285. original_jitcell_token = loop.original_jitcell_token
  286. loopname = jitdriver_sd.warmstate.get_location_str(greenkey)
  287. globaldata = metainterp_sd.globaldata
  288. original_jitcell_token.number = n = globaldata.loopnumbering
  289. globaldata.loopnumbering += 1
  290. if not we_are_translated():
  291. show_procedures(metainterp_sd, loop)
  292. loop.check_consistency()
  293. if metainterp_sd.warmrunnerdesc is not None:
  294. hooks = metainterp_sd.warmrunnerdesc.hooks
  295. debug_info = JitDebugInfo(jitdriver_sd, metainterp_sd.logger_ops,
  296. original_jitcell_token, loop.operations,
  297. type, greenkey)
  298. hooks.before_compile(debug_info)
  299. else:
  300. debug_info = None
  301. hooks = None
  302. operations = get_deep_immutable_oplist(loop.operations)
  303. metainterp_sd.profiler.start_backend()
  304. debug_start("jit-backend")
  305. try:
  306. asminfo = do_compile_loop(metainterp_sd, loop.inputargs,
  307. operations, original_jitcell_token,
  308. name=loopname)
  309. finally:
  310. debug_stop("jit-backend")
  311. metainterp_sd.profiler.end_backend()
  312. if hooks is not None:
  313. debug_info.asminfo = asminfo
  314. hooks.after_compile(debug_info)
  315. metainterp_sd.stats.add_new_loop(loop)
  316. if not we_are_translated():
  317. metainterp_sd.stats.compiled()
  318. metainterp_sd.log("compiled new " + type)
  319. #
  320. if asminfo is not None:
  321. ops_offset = asminfo.ops_offset
  322. else:
  323. ops_offset = None
  324. metainterp_sd.logger_ops.log_loop(loop.inputargs, loop.operations, n,
  325. type, ops_offset,
  326. name=loopname)
  327. #
  328. if metainterp_sd.warmrunnerdesc is not None: # for tests
  329. metainterp_sd.warmrunnerdesc.memory_manager.keep_loop_alive(original_jitcell_token)
  330. def send_bridge_to_backend(jitdriver_sd, metainterp_sd, faildescr, inputargs,
  331. operations, original_loop_token):
  332. if not we_are_translated():
  333. show_procedures(metainterp_sd)
  334. seen = dict.fromkeys(inputargs)
  335. TreeLoop.check_consistency_of_branch(operations, seen)
  336. if metainterp_sd.warmrunnerdesc is not None:
  337. hooks = metainterp_sd.warmrunnerdesc.hooks
  338. debug_info = JitDebugInfo(jitdriver_sd, metainterp_sd.logger_ops,
  339. original_loop_token, operations, 'bridge',
  340. fail_descr=faildescr)
  341. hooks.before_compile_bridge(debug_info)
  342. else:
  343. hooks = None
  344. debug_info = None
  345. operations = get_deep_immutable_oplist(operations)
  346. metainterp_sd.profiler.start_backend()
  347. debug_start("jit-backend")
  348. try:
  349. asminfo = do_compile_bridge(metainterp_sd, faildescr, inputargs,
  350. operations,
  351. original_loop_token)
  352. finally:
  353. debug_stop("jit-backend")
  354. metainterp_sd.profiler.end_backend()
  355. if hooks is not None:
  356. debug_info.asminfo = asminfo
  357. hooks.after_compile_bridge(debug_info)
  358. if not we_are_translated():
  359. metainterp_sd.stats.compiled()
  360. metainterp_sd.log("compiled new bridge")
  361. #
  362. if asminfo is not None:
  363. ops_offset = asminfo.ops_offset
  364. else:
  365. ops_offset = None
  366. metainterp_sd.logger_ops.log_bridge(inputargs, operations, None, faildescr,
  367. ops_offset)
  368. #
  369. #if metainterp_sd.warmrunnerdesc is not None: # for tests
  370. # metainterp_sd.warmrunnerdesc.memory_manager.keep_loop_alive(
  371. # original_loop_token)
  372. # ____________________________________________________________
  373. class _DoneWithThisFrameDescr(AbstractFailDescr):
  374. final_descr = True
  375. class DoneWithThisFrameDescrVoid(_DoneWithThisFrameDescr):
  376. def handle_fail(self, deadframe, metainterp_sd, jitdriver_sd):
  377. assert jitdriver_sd.result_type == history.VOID
  378. raise metainterp_sd.DoneWithThisFrameVoid()
  379. class DoneWithThisFrameDescrInt(_DoneWithThisFrameDescr):
  380. def handle_fail(self, deadframe, metainterp_sd, jitdriver_sd):
  381. assert jitdriver_sd.result_type == history.INT
  382. result = metainterp_sd.cpu.get_int_value(deadframe, 0)
  383. raise metainterp_sd.DoneWithThisFrameInt(result)
  384. class DoneWithThisFrameDescrRef(_DoneWithThisFrameDescr):
  385. def handle_fail(self, deadframe, metainterp_sd, jitdriver_sd):
  386. assert jitdriver_sd.result_type == history.REF
  387. cpu = metainterp_sd.cpu
  388. result = cpu.get_ref_value(deadframe, 0)
  389. raise metainterp_sd.DoneWithThisFrameRef(cpu, result)
  390. class DoneWithThisFrameDescrFloat(_DoneWithThisFrameDescr):
  391. def handle_fail(self, deadframe, metainterp_sd, jitdriver_sd):
  392. assert jitdriver_sd.result_type == history.FLOAT
  393. result = metainterp_sd.cpu.get_float_value(deadframe, 0)
  394. raise metainterp_sd.DoneWithThisFrameFloat(result)
  395. class ExitFrameWithExceptionDescrRef(_DoneWithThisFrameDescr):
  396. def handle_fail(self, deadframe, metainterp_sd, jitdriver_sd):
  397. cpu = metainterp_sd.cpu
  398. value = cpu.get_ref_value(deadframe, 0)
  399. raise metainterp_sd.ExitFrameWithExceptionRef(cpu, value)
  400. class TerminatingLoopToken(JitCellToken): # FIXME: kill?
  401. terminating = True
  402. def __init__(self, nargs, finishdescr):
  403. self.finishdescr = finishdescr
  404. def make_done_loop_tokens():
  405. done_with_this_frame_descr_void = DoneWithThisFrameDescrVoid()
  406. done_with_this_frame_descr_int = DoneWithThisFrameDescrInt()
  407. done_with_this_frame_descr_ref = DoneWithThisFrameDescrRef()
  408. done_with_this_frame_descr_float = DoneWithThisFrameDescrFloat()
  409. exit_frame_with_exception_descr_ref = ExitFrameWithExceptionDescrRef()
  410. # pseudo loop tokens to make the life of optimize.py easier
  411. d = {'loop_tokens_done_with_this_frame_int': [
  412. TerminatingLoopToken(1, done_with_this_frame_descr_int)
  413. ],
  414. 'loop_tokens_done_with_this_frame_ref': [
  415. TerminatingLoopToken(1, done_with_this_frame_descr_ref)
  416. ],
  417. 'loop_tokens_done_with_this_frame_float': [
  418. TerminatingLoopToken(1, done_with_this_frame_descr_float)
  419. ],
  420. 'loop_tokens_done_with_this_frame_void': [
  421. TerminatingLoopToken(0, done_with_this_frame_descr_void)
  422. ],
  423. 'loop_tokens_exit_frame_with_exception_ref': [
  424. TerminatingLoopToken(1, exit_frame_with_exception_descr_ref)
  425. ],
  426. }
  427. d.update(locals())
  428. return d
  429. class ResumeDescr(AbstractFailDescr):
  430. pass
  431. class ResumeGuardDescr(ResumeDescr):
  432. _counter = 0 # on a GUARD_VALUE, there is one counter per value;
  433. _counters = None # they get stored in _counters then.
  434. # this class also gets the following attributes stored by resume.py code
  435. # XXX move all of unused stuff to guard_op, now that we can have
  436. # a separate class, so it does not survive that long
  437. rd_snapshot = None
  438. rd_frame_info_list = None
  439. rd_numb = lltype.nullptr(NUMBERING)
  440. rd_count = 0
  441. rd_consts = None
  442. rd_virtuals = None
  443. rd_pendingfields = lltype.nullptr(PENDINGFIELDSP.TO)
  444. CNT_BASE_MASK = 0x0FFFFFFF # the base counter value
  445. CNT_BUSY_FLAG = 0x10000000 # if set, busy tracing from the guard
  446. CNT_TYPE_MASK = 0x60000000 # mask for the type
  447. CNT_INT = 0x20000000
  448. CNT_REF = 0x40000000
  449. CNT_FLOAT = 0x60000000
  450. def store_final_boxes(self, guard_op, boxes):
  451. guard_op.setfailargs(boxes)
  452. self.rd_count = len(boxes)
  453. self.guard_opnum = guard_op.getopnum()
  454. def make_a_counter_per_value(self, guard_value_op):
  455. assert guard_value_op.getopnum() == rop.GUARD_VALUE
  456. box = guard_value_op.getarg(0)
  457. try:
  458. i = guard_value_op.getfailargs().index(box)
  459. except ValueError:
  460. return # xxx probably very rare
  461. else:
  462. if i > self.CNT_BASE_MASK:
  463. return # probably never, but better safe than sorry
  464. if box.type == history.INT:
  465. cnt = self.CNT_INT
  466. elif box.type == history.REF:
  467. cnt = self.CNT_REF
  468. elif box.type == history.FLOAT:
  469. cnt = self.CNT_FLOAT
  470. else:
  471. assert 0, box.type
  472. assert cnt > self.CNT_BASE_MASK
  473. self._counter = cnt | i
  474. def handle_fail(self, deadframe, metainterp_sd, jitdriver_sd):
  475. if self.must_compile(deadframe, metainterp_sd, jitdriver_sd):
  476. self.start_compiling()
  477. try:
  478. self._trace_and_compile_from_bridge(deadframe, metainterp_sd,
  479. jitdriver_sd)
  480. finally:
  481. self.done_compiling()
  482. else:
  483. from rpython.jit.metainterp.blackhole import resume_in_blackhole
  484. resume_in_blackhole(metainterp_sd, jitdriver_sd, self, deadframe)
  485. assert 0, "unreachable"
  486. def _trace_and_compile_from_bridge(self, deadframe, metainterp_sd,
  487. jitdriver_sd):
  488. # 'jitdriver_sd' corresponds to the outermost one, i.e. the one
  489. # of the jit_merge_point where we started the loop, even if the
  490. # loop itself may contain temporarily recursion into other
  491. # jitdrivers.
  492. from rpython.jit.metainterp.pyjitpl import MetaInterp
  493. metainterp = MetaInterp(metainterp_sd, jitdriver_sd)
  494. metainterp.handle_guard_failure(self, deadframe)
  495. _trace_and_compile_from_bridge._dont_inline_ = True
  496. def must_compile(self, deadframe, metainterp_sd, jitdriver_sd):
  497. trace_eagerness = jitdriver_sd.warmstate.trace_eagerness
  498. #
  499. if self._counter <= self.CNT_BASE_MASK:
  500. # simple case: just counting from 0 to trace_eagerness
  501. self._counter += 1
  502. return self._counter >= trace_eagerness
  503. #
  504. # do we have the BUSY flag? If so, we're tracing right now, e.g. in an
  505. # outer invocation of the same function, so don't trace again for now.
  506. elif self._counter & self.CNT_BUSY_FLAG:
  507. return False
  508. #
  509. else: # we have a GUARD_VALUE that fails. Make a _counters instance
  510. # (only now, when the guard is actually failing at least once),
  511. # and use it to record some statistics about the failing values.
  512. index = self._counter & self.CNT_BASE_MASK
  513. typetag = self._counter & self.CNT_TYPE_MASK
  514. counters = self._counters
  515. if typetag == self.CNT_INT:
  516. intvalue = metainterp_sd.cpu.get_int_value(
  517. deadframe, index)
  518. if counters is None:
  519. self._counters = counters = ResumeGuardCountersInt()
  520. else:
  521. assert isinstance(counters, ResumeGuardCountersInt)
  522. counter = counters.see_int(intvalue)
  523. elif typetag == self.CNT_REF:
  524. refvalue = metainterp_sd.cpu.get_ref_value(
  525. deadframe, index)
  526. if counters is None:
  527. self._counters = counters = ResumeGuardCountersRef()
  528. else:
  529. assert isinstance(counters, ResumeGuardCountersRef)
  530. counter = counters.see_ref(refvalue)
  531. elif typetag == self.CNT_FLOAT:
  532. floatvalue = metainterp_sd.cpu.get_float_value(
  533. deadframe, index)
  534. if counters is None:
  535. self._counters = counters = ResumeGuardCountersFloat()
  536. else:
  537. assert isinstance(counters, ResumeGuardCountersFloat)
  538. counter = counters.see_float(floatvalue)
  539. else:
  540. assert 0, typetag
  541. return counter >= trace_eagerness
  542. def start_compiling(self):
  543. # start tracing and compiling from this guard.
  544. self._counter |= self.CNT_BUSY_FLAG
  545. def done_compiling(self):
  546. # done tracing and compiling from this guard. Either the bridge has
  547. # been successfully compiled, in which case whatever value we store
  548. # in self._counter will not be seen any more, or not, in which case
  549. # we should reset the counter to 0, in order to wait a bit until the
  550. # next attempt.
  551. if self._counter >= 0:
  552. self._counter = 0
  553. self._counters = None
  554. def compile_and_attach(self, metainterp, new_loop):
  555. # We managed to create a bridge. Attach the new operations
  556. # to the corresponding guard_op and compile from there
  557. assert metainterp.resumekey_original_loop_token is not None
  558. new_loop.original_jitcell_token = metainterp.resumekey_original_loop_token
  559. inputargs = metainterp.history.inputargs
  560. if not we_are_translated():
  561. self._debug_suboperations = new_loop.operations
  562. propagate_original_jitcell_token(new_loop)
  563. send_bridge_to_backend(metainterp.jitdriver_sd, metainterp.staticdata,
  564. self, inputargs, new_loop.operations,
  565. new_loop.original_jitcell_token)
  566. def copy_all_attributes_into(self, res):
  567. # XXX a bit ugly to have to list them all here
  568. res.rd_snapshot = self.rd_snapshot
  569. res.rd_frame_info_list = self.rd_frame_info_list
  570. res.rd_numb = self.rd_numb
  571. res.rd_consts = self.rd_consts
  572. res.rd_virtuals = self.rd_virtuals
  573. res.rd_pendingfields = self.rd_pendingfields
  574. res.rd_count = self.rd_count
  575. def _clone_if_mutable(self):
  576. res = ResumeGuardDescr()
  577. self.copy_all_attributes_into(res)
  578. return res
  579. class ResumeGuardNotInvalidated(ResumeGuardDescr):
  580. def _clone_if_mutable(self):
  581. res = ResumeGuardNotInvalidated()
  582. self.copy_all_attributes_into(res)
  583. return res
  584. class ResumeAtPositionDescr(ResumeGuardDescr):
  585. def _clone_if_mutable(self):
  586. res = ResumeAtPositionDescr()
  587. self.copy_all_attributes_into(res)
  588. return res
  589. class AllVirtuals:
  590. llopaque = True
  591. list = [resume.ResumeDataDirectReader.virtual_default] # annotation hack
  592. def __init__(self, list):
  593. self.list = list
  594. def hide(self, cpu):
  595. ptr = cpu.ts.cast_instance_to_base_ref(self)
  596. return cpu.ts.cast_to_ref(ptr)
  597. @staticmethod
  598. def show(cpu, gcref):
  599. from rpython.rtyper.annlowlevel import cast_base_ptr_to_instance
  600. ptr = cpu.ts.cast_to_baseclass(gcref)
  601. return cast_base_ptr_to_instance(AllVirtuals, ptr)
  602. class ResumeGuardForcedDescr(ResumeGuardDescr):
  603. def __init__(self, metainterp_sd, jitdriver_sd):
  604. self.metainterp_sd = metainterp_sd
  605. self.jitdriver_sd = jitdriver_sd
  606. def handle_fail(self, deadframe, metainterp_sd, jitdriver_sd):
  607. # Failures of a GUARD_NOT_FORCED are never compiled, but
  608. # always just blackholed. First fish for the data saved when
  609. # the virtualrefs and virtualizable have been forced by
  610. # handle_async_forcing() just a moment ago.
  611. from rpython.jit.metainterp.blackhole import resume_in_blackhole
  612. hidden_all_virtuals = metainterp_sd.cpu.get_savedata_ref(deadframe)
  613. obj = AllVirtuals.show(metainterp_sd.cpu, hidden_all_virtuals)
  614. all_virtuals = obj.list
  615. if all_virtuals is None:
  616. all_virtuals = []
  617. assert jitdriver_sd is self.jitdriver_sd
  618. resume_in_blackhole(metainterp_sd, jitdriver_sd, self, deadframe,
  619. all_virtuals)
  620. assert 0, "unreachable"
  621. @staticmethod
  622. @dont_look_inside
  623. def force_now(cpu, token):
  624. # Called during a residual call from the assembler, if the code
  625. # actually needs to force one of the virtualrefs or the virtualizable.
  626. # Implemented by forcing *all* virtualrefs and the virtualizable.
  627. # don't interrupt me! If the stack runs out in force_from_resumedata()
  628. # then we have seen cpu.force() but not self.save_data(), leaving in
  629. # an inconsistent state
  630. rstack._stack_criticalcode_start()
  631. try:
  632. deadframe = cpu.force(token)
  633. faildescr = cpu.get_latest_descr(deadframe)
  634. assert isinstance(faildescr, ResumeGuardForcedDescr)
  635. faildescr.handle_async_forcing(deadframe)
  636. finally:
  637. rstack._stack_criticalcode_stop()
  638. def handle_async_forcing(self, deadframe):
  639. from rpython.jit.metainterp.resume import force_from_resumedata
  640. metainterp_sd = self.metainterp_sd
  641. vinfo = self.jitdriver_sd.virtualizable_info
  642. ginfo = self.jitdriver_sd.greenfield_info
  643. all_virtuals = force_from_resumedata(metainterp_sd, self, deadframe,
  644. vinfo, ginfo)
  645. # The virtualizable data was stored on the real virtualizable above.
  646. # Handle all_virtuals: keep them for later blackholing from the
  647. # future failure of the GUARD_NOT_FORCED
  648. obj = AllVirtuals(all_virtuals)
  649. hidden_all_virtuals = obj.hide(metainterp_sd.cpu)
  650. metainterp_sd.cpu.set_savedata_ref(deadframe, hidden_all_virtuals)
  651. def _clone_if_mutable(self):
  652. res = ResumeGuardForcedDescr(self.metainterp_sd,
  653. self.jitdriver_sd)
  654. self.copy_all_attributes_into(res)
  655. return res
  656. class AbstractResumeGuardCounters(object):
  657. # Completely custom algorithm for now: keep 5 pairs (value, counter),
  658. # and when we need more, we discard the middle pair (middle in the
  659. # current value of the counter). That way, we tend to keep the
  660. # values with a high counter, but also we avoid always throwing away
  661. # the most recently added value. **THIS ALGO MUST GO AWAY AT SOME POINT**
  662. pass
  663. def _see(self, newvalue):
  664. # find and update an existing counter
  665. unused = -1
  666. for i in range(5):
  667. cnt = self.counters[i]
  668. if cnt:
  669. if self.values[i] == newvalue:
  670. cnt += 1
  671. self.counters[i] = cnt
  672. return cnt
  673. else:
  674. unused = i
  675. # not found. Use a previously unused entry, if there is one
  676. if unused >= 0:
  677. self.counters[unused] = 1
  678. self.values[unused] = newvalue
  679. return 1
  680. # no unused entry. Overwrite the middle one. Computed with indices
  681. # a, b, c meaning the highest, second highest, and third highest
  682. # entries.
  683. a = 0
  684. b = c = -1
  685. for i in range(1, 5):
  686. if self.counters[i] > self.counters[a]:
  687. c = b; b = a; a = i
  688. elif b < 0 or self.counters[i] > self.counters[b]:
  689. c = b; b = i
  690. elif c < 0 or self.counters[i] > self.counters[c]:
  691. c = i
  692. self.counters[c] = 1
  693. self.values[c] = newvalue
  694. return 1
  695. class ResumeGuardCountersInt(AbstractResumeGuardCounters):
  696. def __init__(self):
  697. self.counters = [0] * 5
  698. self.values = [0] * 5
  699. see_int = func_with_new_name(_see, 'see_int')
  700. class ResumeGuardCountersRef(AbstractResumeGuardCounters):
  701. def __init__(self):
  702. self.counters = [0] * 5
  703. self.values = [history.ConstPtr.value] * 5
  704. see_ref = func_with_new_name(_see, 'see_ref')
  705. class ResumeGuardCountersFloat(AbstractResumeGuardCounters):
  706. def __init__(self):
  707. self.counters = [0] * 5
  708. self.values = [longlong.ZEROF] * 5
  709. see_float = func_with_new_name(_see, 'see_float')
  710. class ResumeFromInterpDescr(ResumeDescr):
  711. def __init__(self, original_greenkey):
  712. self.original_greenkey = original_greenkey
  713. def compile_and_attach(self, metainterp, new_loop):
  714. # We managed to create a bridge going from the interpreter
  715. # to previously-compiled code. We keep 'new_loop', which is not
  716. # a loop at all but ends in a jump to the target loop. It starts
  717. # with completely unoptimized arguments, as in the interpreter.
  718. metainterp_sd = metainterp.staticdata
  719. jitdriver_sd = metainterp.jitdriver_sd
  720. redargs = new_loop.inputargs
  721. new_loop.original_jitcell_token = jitcell_token = make_jitcell_token(jitdriver_sd)
  722. propagate_original_jitcell_token(new_loop)
  723. send_loop_to_backend(self.original_greenkey, metainterp.jitdriver_sd,
  724. metainterp_sd, new_loop, "entry bridge")
  725. # send the new_loop to warmspot.py, to be called directly the next time
  726. jitdriver_sd.warmstate.attach_procedure_to_interp(
  727. self.original_greenkey, jitcell_token)
  728. metainterp_sd.stats.add_jitcell_token(jitcell_token)
  729. def compile_trace(metainterp, resumekey, resume_at_jump_descr=None):
  730. """Try to compile a new bridge leading from the beginning of the history
  731. to some existing place.
  732. """
  733. from rpython.jit.metainterp.optimizeopt import optimize_trace
  734. # The history contains new operations to attach as the code for the
  735. # failure of 'resumekey.guard_op'.
  736. #
  737. # Attempt to use optimize_bridge(). This may return None in case
  738. # it does not work -- i.e. none of the existing old_loop_tokens match.
  739. new_trace = create_empty_loop(metainterp)
  740. new_trace.inputargs = inputargs = metainterp.history.inputargs[:]
  741. # clone ops, as optimize_bridge can mutate the ops
  742. new_trace.operations = [op.clone() for op in metainterp.history.operations]
  743. new_trace.resume_at_jump_descr = resume_at_jump_descr
  744. metainterp_sd = metainterp.staticdata
  745. state = metainterp.jitdriver_sd.warmstate
  746. if isinstance(resumekey, ResumeAtPositionDescr):
  747. inline_short_preamble = False
  748. else:
  749. inline_short_preamble = True
  750. try:
  751. optimize_trace(metainterp_sd, new_trace, state.enable_opts, inline_short_preamble)
  752. except InvalidLoop:
  753. debug_print("compile_new_bridge: got an InvalidLoop")
  754. # XXX I am fairly convinced that optimize_bridge cannot actually raise
  755. # InvalidLoop
  756. debug_print('InvalidLoop in compile_new_bridge')
  757. return None
  758. if new_trace.operations[-1].getopnum() != rop.LABEL:
  759. # We managed to create a bridge. Dispatch to resumekey to
  760. # know exactly what we must do (ResumeGuardDescr/ResumeFromInterpDescr)
  761. target_token = new_trace.operations[-1].getdescr()
  762. resumekey.compile_and_attach(metainterp, new_trace)
  763. record_loop_or_bridge(metainterp_sd, new_trace)
  764. return target_token
  765. else:
  766. metainterp.retrace_needed(new_trace)
  767. return None
  768. # ____________________________________________________________
  769. memory_error = MemoryError()
  770. class PropagateExceptionDescr(AbstractFailDescr):
  771. def handle_fail(self, deadframe, metainterp_sd, jitdriver_sd):
  772. cpu = metainterp_sd.cpu
  773. exception = cpu.grab_exc_value(deadframe)
  774. if not exception:
  775. exception = cast_instance_to_gcref(memory_error)
  776. assert exception, "PropagateExceptionDescr: no exception??"
  777. raise metainterp_sd.ExitFrameWithExceptionRef(cpu, exception)
  778. def compile_tmp_callback(cpu, jitdriver_sd, greenboxes, redargtypes,
  779. memory_manager=None):
  780. """Make a LoopToken that corresponds to assembler code that just
  781. calls back the interpreter. Used temporarily: a fully compiled
  782. version of the code may end up replacing it.
  783. """
  784. jitcell_token = make_jitcell_token(jitdriver_sd)
  785. nb_red_args = jitdriver_sd.num_red_args
  786. assert len(redargtypes) == nb_red_args
  787. inputargs = []
  788. for kind in redargtypes:
  789. if kind == history.INT: box = BoxInt()
  790. elif kind == history.REF: box = BoxPtr()
  791. elif kind == history.FLOAT: box = BoxFloat()
  792. else: raise AssertionError
  793. inputargs.append(box)
  794. k = jitdriver_sd.portal_runner_adr
  795. funcbox = history.ConstInt(heaptracker.adr2int(k))
  796. callargs = [funcbox] + greenboxes + inputargs
  797. #
  798. result_type = jitdriver_sd.result_type
  799. if result_type == history.INT:
  800. result = BoxInt()
  801. elif result_type == history.REF:
  802. result = BoxPtr()
  803. elif result_type == history.FLOAT:
  804. result = BoxFloat()
  805. elif result_type == history.VOID:
  806. result = None
  807. else:
  808. assert 0, "bad result_type"
  809. if result is not None:
  810. finishargs = [result]
  811. else:
  812. finishargs = []
  813. #
  814. jd = jitdriver_sd
  815. faildescr = jitdriver_sd.propagate_exc_descr
  816. operations = [
  817. ResOperation(rop.CALL, callargs, result, descr=jd.portal_calldescr),
  818. ResOperation(rop.GUARD_NO_EXCEPTION, [], None, descr=faildescr),
  819. ResOperation(rop.FINISH, finishargs, None, descr=jd.portal_finishtoken)
  820. ]
  821. operations[1].setfailargs([])
  822. operations = get_deep_immutable_oplist(operations)
  823. cpu.compile_loop(inputargs, operations, jitcell_token, log=False)
  824. if memory_manager is not None: # for tests
  825. memory_manager.keep_loop_alive(jitcell_token)
  826. return jitcell_token