PageRenderTime 59ms CodeModel.GetById 24ms RepoModel.GetById 0ms app.codeStats 0ms

/pypy/jit/metainterp/compile.py

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