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

/rpython/jit/metainterp/compile.py

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