PageRenderTime 58ms CodeModel.GetById 18ms RepoModel.GetById 0ms app.codeStats 0ms

/pypy/jit/metainterp/compile.py

https://bitbucket.org/shomah4a/pypy
Python | 855 lines | 681 code | 85 blank | 89 comment | 116 complexity | 4b99ae018ba02264edc522ef76cb51e4 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.conftest import option
  9. from pypy.tool.sourcetools import func_with_new_name
  10. from pypy.jit.metainterp.resoperation import ResOperation, rop, get_deep_immutable_oplist
  11. from pypy.jit.metainterp.history import TreeLoop, Box, History, JitCellToken, TargetToken
  12. from pypy.jit.metainterp.history import AbstractFailDescr, BoxInt
  13. from pypy.jit.metainterp.history import BoxPtr, BoxObj, BoxFloat, Const, ConstInt
  14. from pypy.jit.metainterp import history
  15. from pypy.jit.metainterp.typesystem import llhelper, oohelper
  16. from pypy.jit.metainterp.optimize import InvalidLoop
  17. from pypy.jit.metainterp.inliner import Inliner
  18. from pypy.jit.metainterp.resume import NUMBERING, PENDINGFIELDSP
  19. from pypy.jit.codewriter import heaptracker, longlong
  20. def giveup():
  21. from pypy.jit.metainterp.pyjitpl import SwitchToBlackhole
  22. from pypy.jit.metainterp.jitprof import ABORT_BRIDGE
  23. raise SwitchToBlackhole(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. 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._descr = None # 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._descr = None # 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. start_resumedescr, full_preamble_needed=True):
  100. """Try to compile a new procedure by closing the current history back
  101. to the first operation.
  102. """
  103. from pypy.jit.metainterp.optimizeopt import optimize_trace
  104. history = metainterp.history
  105. metainterp_sd = metainterp.staticdata
  106. jitdriver_sd = metainterp.jitdriver_sd
  107. if False:
  108. part = partial_trace
  109. assert False
  110. procedur_token = metainterp.get_procedure_token(greenkey)
  111. assert procedure_token
  112. all_target_tokens = []
  113. else:
  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.start_resumedescr = start_resumedescr
  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.JUMP, jumpargs, None, descr=jitcell_token)]
  122. try:
  123. optimize_trace(metainterp_sd, part, jitdriver_sd.warmstate.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, jitdriver_sd.warmstate.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. label.original_jitcell_token = jitcell_token
  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. 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. start_resumedescr, partial_trace, resumekey):
  171. """Try to compile a new procedure by closing the current history back
  172. to the first operation.
  173. """
  174. from pypy.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.start_resumedescr = start_resumedescr
  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. #return None # XXX: Dissable for now
  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, target_token.exported_state.jump_args,
  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. loop = partial_trace
  213. loop.operations = loop.operations[:-1] + part.operations
  214. quasi_immutable_deps = {}
  215. if loop.quasi_immutable_deps:
  216. quasi_immutable_deps.update(loop.quasi_immutable_deps)
  217. if part.quasi_immutable_deps:
  218. quasi_immutable_deps.update(part.quasi_immutable_deps)
  219. if quasi_immutable_deps:
  220. loop.quasi_immutable_deps = quasi_immutable_deps
  221. for box in loop.inputargs:
  222. assert isinstance(box, Box)
  223. target_token = loop.operations[-1].getdescr()
  224. resumekey.compile_and_attach(metainterp, loop)
  225. target_token = label.getdescr()
  226. assert isinstance(target_token, TargetToken)
  227. target_token.original_jitcell_token = loop.original_jitcell_token
  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 send_loop_to_backend(greenkey, jitdriver_sd, metainterp_sd, loop, type):
  263. vinfo = jitdriver_sd.virtualizable_info
  264. if vinfo is not None:
  265. patch_new_loop_to_load_virtualizable_fields(loop, jitdriver_sd)
  266. original_jitcell_token = loop.original_jitcell_token
  267. jitdriver_sd.on_compile(metainterp_sd.logger_ops, original_jitcell_token,
  268. loop.operations, type, greenkey)
  269. loopname = jitdriver_sd.warmstate.get_location_str(greenkey)
  270. globaldata = metainterp_sd.globaldata
  271. original_jitcell_token.number = n = globaldata.loopnumbering
  272. globaldata.loopnumbering += 1
  273. if not we_are_translated():
  274. show_procedures(metainterp_sd, loop)
  275. loop.check_consistency()
  276. operations = get_deep_immutable_oplist(loop.operations)
  277. metainterp_sd.profiler.start_backend()
  278. debug_start("jit-backend")
  279. try:
  280. ops_offset = metainterp_sd.cpu.compile_loop(loop.inputargs, operations,
  281. original_jitcell_token, name=loopname)
  282. finally:
  283. debug_stop("jit-backend")
  284. metainterp_sd.profiler.end_backend()
  285. metainterp_sd.stats.add_new_loop(loop)
  286. if not we_are_translated():
  287. metainterp_sd.stats.compiled()
  288. metainterp_sd.log("compiled new " + type)
  289. #
  290. metainterp_sd.logger_ops.log_loop(loop.inputargs, loop.operations, n, type, ops_offset)
  291. #
  292. if metainterp_sd.warmrunnerdesc is not None: # for tests
  293. metainterp_sd.warmrunnerdesc.memory_manager.keep_loop_alive(original_jitcell_token)
  294. def send_bridge_to_backend(jitdriver_sd, metainterp_sd, faildescr, inputargs,
  295. operations, original_loop_token):
  296. n = metainterp_sd.cpu.get_fail_descr_number(faildescr)
  297. jitdriver_sd.on_compile_bridge(metainterp_sd.logger_ops,
  298. original_loop_token, operations, n)
  299. if not we_are_translated():
  300. show_procedures(metainterp_sd)
  301. seen = dict.fromkeys(inputargs)
  302. TreeLoop.check_consistency_of_branch(operations, seen)
  303. metainterp_sd.profiler.start_backend()
  304. operations = get_deep_immutable_oplist(operations)
  305. debug_start("jit-backend")
  306. try:
  307. ops_offset = metainterp_sd.cpu.compile_bridge(faildescr, inputargs, operations,
  308. original_loop_token)
  309. finally:
  310. debug_stop("jit-backend")
  311. metainterp_sd.profiler.end_backend()
  312. if not we_are_translated():
  313. metainterp_sd.stats.compiled()
  314. metainterp_sd.log("compiled new bridge")
  315. #
  316. metainterp_sd.logger_ops.log_bridge(inputargs, operations, n, ops_offset)
  317. #
  318. #if metainterp_sd.warmrunnerdesc is not None: # for tests
  319. # metainterp_sd.warmrunnerdesc.memory_manager.keep_loop_alive(
  320. # original_loop_token)
  321. # ____________________________________________________________
  322. class _DoneWithThisFrameDescr(AbstractFailDescr):
  323. pass
  324. class DoneWithThisFrameDescrVoid(_DoneWithThisFrameDescr):
  325. def handle_fail(self, metainterp_sd, jitdriver_sd):
  326. assert jitdriver_sd.result_type == history.VOID
  327. raise metainterp_sd.DoneWithThisFrameVoid()
  328. class DoneWithThisFrameDescrInt(_DoneWithThisFrameDescr):
  329. def handle_fail(self, metainterp_sd, jitdriver_sd):
  330. assert jitdriver_sd.result_type == history.INT
  331. result = metainterp_sd.cpu.get_latest_value_int(0)
  332. raise metainterp_sd.DoneWithThisFrameInt(result)
  333. class DoneWithThisFrameDescrRef(_DoneWithThisFrameDescr):
  334. def handle_fail(self, metainterp_sd, jitdriver_sd):
  335. assert jitdriver_sd.result_type == history.REF
  336. cpu = metainterp_sd.cpu
  337. result = cpu.get_latest_value_ref(0)
  338. cpu.clear_latest_values(1)
  339. raise metainterp_sd.DoneWithThisFrameRef(cpu, result)
  340. class DoneWithThisFrameDescrFloat(_DoneWithThisFrameDescr):
  341. def handle_fail(self, metainterp_sd, jitdriver_sd):
  342. assert jitdriver_sd.result_type == history.FLOAT
  343. result = metainterp_sd.cpu.get_latest_value_float(0)
  344. raise metainterp_sd.DoneWithThisFrameFloat(result)
  345. class ExitFrameWithExceptionDescrRef(_DoneWithThisFrameDescr):
  346. def handle_fail(self, metainterp_sd, jitdriver_sd):
  347. cpu = metainterp_sd.cpu
  348. value = cpu.get_latest_value_ref(0)
  349. cpu.clear_latest_values(1)
  350. raise metainterp_sd.ExitFrameWithExceptionRef(cpu, value)
  351. class TerminatingLoopToken(JitCellToken): # FIXME: kill?
  352. terminating = True
  353. def __init__(self, nargs, finishdescr):
  354. self.finishdescr = finishdescr
  355. def make_done_loop_tokens():
  356. done_with_this_frame_descr_void = DoneWithThisFrameDescrVoid()
  357. done_with_this_frame_descr_int = DoneWithThisFrameDescrInt()
  358. done_with_this_frame_descr_ref = DoneWithThisFrameDescrRef()
  359. done_with_this_frame_descr_float = DoneWithThisFrameDescrFloat()
  360. exit_frame_with_exception_descr_ref = ExitFrameWithExceptionDescrRef()
  361. # pseudo loop tokens to make the life of optimize.py easier
  362. return {'loop_tokens_done_with_this_frame_int': [
  363. TerminatingLoopToken(1, done_with_this_frame_descr_int)
  364. ],
  365. 'loop_tokens_done_with_this_frame_ref': [
  366. TerminatingLoopToken(1, done_with_this_frame_descr_ref)
  367. ],
  368. 'loop_tokens_done_with_this_frame_float': [
  369. TerminatingLoopToken(1, done_with_this_frame_descr_float)
  370. ],
  371. 'loop_tokens_done_with_this_frame_void': [
  372. TerminatingLoopToken(0, done_with_this_frame_descr_void)
  373. ],
  374. 'loop_tokens_exit_frame_with_exception_ref': [
  375. TerminatingLoopToken(1, exit_frame_with_exception_descr_ref)
  376. ],
  377. }
  378. class ResumeDescr(AbstractFailDescr):
  379. pass
  380. class ResumeGuardDescr(ResumeDescr):
  381. _counter = 0 # on a GUARD_VALUE, there is one counter per value;
  382. _counters = None # they get stored in _counters then.
  383. # this class also gets the following attributes stored by resume.py code
  384. rd_snapshot = None
  385. rd_frame_info_list = None
  386. rd_numb = lltype.nullptr(NUMBERING)
  387. rd_consts = None
  388. rd_virtuals = None
  389. rd_pendingfields = lltype.nullptr(PENDINGFIELDSP.TO)
  390. CNT_BASE_MASK = 0x0FFFFFFF # the base counter value
  391. CNT_BUSY_FLAG = 0x10000000 # if set, busy tracing from the guard
  392. CNT_TYPE_MASK = 0x60000000 # mask for the type
  393. CNT_INT = 0x20000000
  394. CNT_REF = 0x40000000
  395. CNT_FLOAT = 0x60000000
  396. def store_final_boxes(self, guard_op, boxes):
  397. guard_op.setfailargs(boxes)
  398. self.guard_opnum = guard_op.getopnum()
  399. def make_a_counter_per_value(self, guard_value_op):
  400. assert guard_value_op.getopnum() == rop.GUARD_VALUE
  401. box = guard_value_op.getarg(0)
  402. try:
  403. i = guard_value_op.getfailargs().index(box)
  404. except ValueError:
  405. return # xxx probably very rare
  406. else:
  407. if i > self.CNT_BASE_MASK:
  408. return # probably never, but better safe than sorry
  409. if box.type == history.INT:
  410. cnt = self.CNT_INT
  411. elif box.type == history.REF:
  412. cnt = self.CNT_REF
  413. elif box.type == history.FLOAT:
  414. cnt = self.CNT_FLOAT
  415. else:
  416. assert 0, box.type
  417. assert cnt > self.CNT_BASE_MASK
  418. self._counter = cnt | i
  419. def handle_fail(self, metainterp_sd, jitdriver_sd):
  420. if self.must_compile(metainterp_sd, jitdriver_sd):
  421. self.start_compiling()
  422. try:
  423. self._trace_and_compile_from_bridge(metainterp_sd,
  424. jitdriver_sd)
  425. finally:
  426. self.done_compiling()
  427. else:
  428. from pypy.jit.metainterp.blackhole import resume_in_blackhole
  429. resume_in_blackhole(metainterp_sd, jitdriver_sd, self)
  430. assert 0, "unreachable"
  431. def _trace_and_compile_from_bridge(self, metainterp_sd, jitdriver_sd):
  432. # 'jitdriver_sd' corresponds to the outermost one, i.e. the one
  433. # of the jit_merge_point where we started the loop, even if the
  434. # loop itself may contain temporarily recursion into other
  435. # jitdrivers.
  436. from pypy.jit.metainterp.pyjitpl import MetaInterp
  437. metainterp = MetaInterp(metainterp_sd, jitdriver_sd)
  438. metainterp.handle_guard_failure(self)
  439. _trace_and_compile_from_bridge._dont_inline_ = True
  440. def must_compile(self, metainterp_sd, jitdriver_sd):
  441. trace_eagerness = jitdriver_sd.warmstate.trace_eagerness
  442. #
  443. if self._counter <= self.CNT_BASE_MASK:
  444. # simple case: just counting from 0 to trace_eagerness
  445. self._counter += 1
  446. return self._counter >= trace_eagerness
  447. #
  448. # do we have the BUSY flag? If so, we're tracing right now, e.g. in an
  449. # outer invocation of the same function, so don't trace again for now.
  450. elif self._counter & self.CNT_BUSY_FLAG:
  451. return False
  452. #
  453. else: # we have a GUARD_VALUE that fails. Make a _counters instance
  454. # (only now, when the guard is actually failing at least once),
  455. # and use it to record some statistics about the failing values.
  456. index = self._counter & self.CNT_BASE_MASK
  457. typetag = self._counter & self.CNT_TYPE_MASK
  458. counters = self._counters
  459. if typetag == self.CNT_INT:
  460. intvalue = metainterp_sd.cpu.get_latest_value_int(index)
  461. if counters is None:
  462. self._counters = counters = ResumeGuardCountersInt()
  463. else:
  464. assert isinstance(counters, ResumeGuardCountersInt)
  465. counter = counters.see_int(intvalue)
  466. elif typetag == self.CNT_REF:
  467. refvalue = metainterp_sd.cpu.get_latest_value_ref(index)
  468. if counters is None:
  469. self._counters = counters = ResumeGuardCountersRef()
  470. else:
  471. assert isinstance(counters, ResumeGuardCountersRef)
  472. counter = counters.see_ref(refvalue)
  473. elif typetag == self.CNT_FLOAT:
  474. floatvalue = metainterp_sd.cpu.get_latest_value_float(index)
  475. if counters is None:
  476. self._counters = counters = ResumeGuardCountersFloat()
  477. else:
  478. assert isinstance(counters, ResumeGuardCountersFloat)
  479. counter = counters.see_float(floatvalue)
  480. else:
  481. assert 0, typetag
  482. return counter >= trace_eagerness
  483. def start_compiling(self):
  484. # start tracing and compiling from this guard.
  485. self._counter |= self.CNT_BUSY_FLAG
  486. def done_compiling(self):
  487. # done tracing and compiling from this guard. Either the bridge has
  488. # been successfully compiled, in which case whatever value we store
  489. # in self._counter will not be seen any more, or not, in which case
  490. # we should reset the counter to 0, in order to wait a bit until the
  491. # next attempt.
  492. if self._counter >= 0:
  493. self._counter = 0
  494. self._counters = None
  495. def compile_and_attach(self, metainterp, new_loop):
  496. # We managed to create a bridge. Attach the new operations
  497. # to the corresponding guard_op and compile from there
  498. assert metainterp.resumekey_original_loop_token is not None
  499. new_loop.original_jitcell_token = metainterp.resumekey_original_loop_token
  500. inputargs = metainterp.history.inputargs
  501. if not we_are_translated():
  502. self._debug_suboperations = new_loop.operations
  503. send_bridge_to_backend(metainterp.jitdriver_sd, metainterp.staticdata,
  504. self, inputargs, new_loop.operations,
  505. new_loop.original_jitcell_token)
  506. def copy_all_attributes_into(self, res):
  507. # XXX a bit ugly to have to list them all here
  508. res.rd_snapshot = self.rd_snapshot
  509. res.rd_frame_info_list = self.rd_frame_info_list
  510. res.rd_numb = self.rd_numb
  511. res.rd_consts = self.rd_consts
  512. res.rd_virtuals = self.rd_virtuals
  513. res.rd_pendingfields = self.rd_pendingfields
  514. def _clone_if_mutable(self):
  515. res = ResumeGuardDescr()
  516. self.copy_all_attributes_into(res)
  517. return res
  518. class ResumeGuardNotInvalidated(ResumeGuardDescr):
  519. def _clone_if_mutable(self):
  520. res = ResumeGuardNotInvalidated()
  521. self.copy_all_attributes_into(res)
  522. return res
  523. class ResumeAtPositionDescr(ResumeGuardDescr):
  524. def _clone_if_mutable(self):
  525. res = ResumeAtPositionDescr()
  526. self.copy_all_attributes_into(res)
  527. return res
  528. class ResumeGuardForcedDescr(ResumeGuardDescr):
  529. def __init__(self, metainterp_sd, jitdriver_sd):
  530. self.metainterp_sd = metainterp_sd
  531. self.jitdriver_sd = jitdriver_sd
  532. def handle_fail(self, metainterp_sd, jitdriver_sd):
  533. # Failures of a GUARD_NOT_FORCED are never compiled, but
  534. # always just blackholed. First fish for the data saved when
  535. # the virtualrefs and virtualizable have been forced by
  536. # handle_async_forcing() just a moment ago.
  537. from pypy.jit.metainterp.blackhole import resume_in_blackhole
  538. token = metainterp_sd.cpu.get_latest_force_token()
  539. all_virtuals = self.fetch_data(token)
  540. if all_virtuals is None:
  541. all_virtuals = []
  542. assert jitdriver_sd is self.jitdriver_sd
  543. resume_in_blackhole(metainterp_sd, jitdriver_sd, self, all_virtuals)
  544. assert 0, "unreachable"
  545. @staticmethod
  546. def force_now(cpu, token):
  547. # Called during a residual call from the assembler, if the code
  548. # actually needs to force one of the virtualrefs or the virtualizable.
  549. # Implemented by forcing *all* virtualrefs and the virtualizable.
  550. # don't interrupt me! If the stack runs out in force_from_resumedata()
  551. # then we have seen cpu.force() but not self.save_data(), leaving in
  552. # an inconsistent state
  553. rstack._stack_criticalcode_start()
  554. try:
  555. faildescr = cpu.force(token)
  556. assert isinstance(faildescr, ResumeGuardForcedDescr)
  557. faildescr.handle_async_forcing(token)
  558. finally:
  559. rstack._stack_criticalcode_stop()
  560. def handle_async_forcing(self, force_token):
  561. from pypy.jit.metainterp.resume import force_from_resumedata
  562. metainterp_sd = self.metainterp_sd
  563. vinfo = self.jitdriver_sd.virtualizable_info
  564. ginfo = self.jitdriver_sd.greenfield_info
  565. all_virtuals = force_from_resumedata(metainterp_sd, self, vinfo, ginfo)
  566. # The virtualizable data was stored on the real virtualizable above.
  567. # Handle all_virtuals: keep them for later blackholing from the
  568. # future failure of the GUARD_NOT_FORCED
  569. self.save_data(force_token, all_virtuals)
  570. def save_data(self, key, value):
  571. globaldata = self.metainterp_sd.globaldata
  572. if we_are_translated():
  573. assert key not in globaldata.resume_virtuals
  574. globaldata.resume_virtuals[key] = value
  575. else:
  576. rv = globaldata.resume_virtuals_not_translated
  577. for key1, value1 in rv:
  578. assert key1 != key
  579. rv.append((key, value))
  580. def fetch_data(self, key):
  581. globaldata = self.metainterp_sd.globaldata
  582. if we_are_translated():
  583. assert key in globaldata.resume_virtuals
  584. data = globaldata.resume_virtuals[key]
  585. del globaldata.resume_virtuals[key]
  586. else:
  587. rv = globaldata.resume_virtuals_not_translated
  588. for i in range(len(rv)):
  589. if rv[i][0] == key:
  590. data = rv[i][1]
  591. del rv[i]
  592. break
  593. else:
  594. assert 0, "not found: %r" % (key,)
  595. return data
  596. def _clone_if_mutable(self):
  597. res = ResumeGuardForcedDescr(self.metainterp_sd,
  598. self.jitdriver_sd)
  599. self.copy_all_attributes_into(res)
  600. return res
  601. class AbstractResumeGuardCounters(object):
  602. # Completely custom algorithm for now: keep 5 pairs (value, counter),
  603. # and when we need more, we discard the middle pair (middle in the
  604. # current value of the counter). That way, we tend to keep the
  605. # values with a high counter, but also we avoid always throwing away
  606. # the most recently added value. **THIS ALGO MUST GO AWAY AT SOME POINT**
  607. pass
  608. def _see(self, newvalue):
  609. # find and update an existing counter
  610. unused = -1
  611. for i in range(5):
  612. cnt = self.counters[i]
  613. if cnt:
  614. if self.values[i] == newvalue:
  615. cnt += 1
  616. self.counters[i] = cnt
  617. return cnt
  618. else:
  619. unused = i
  620. # not found. Use a previously unused entry, if there is one
  621. if unused >= 0:
  622. self.counters[unused] = 1
  623. self.values[unused] = newvalue
  624. return 1
  625. # no unused entry. Overwrite the middle one. Computed with indices
  626. # a, b, c meaning the highest, second highest, and third highest
  627. # entries.
  628. a = 0
  629. b = c = -1
  630. for i in range(1, 5):
  631. if self.counters[i] > self.counters[a]:
  632. c = b; b = a; a = i
  633. elif b < 0 or self.counters[i] > self.counters[b]:
  634. c = b; b = i
  635. elif c < 0 or self.counters[i] > self.counters[c]:
  636. c = i
  637. self.counters[c] = 1
  638. self.values[c] = newvalue
  639. return 1
  640. class ResumeGuardCountersInt(AbstractResumeGuardCounters):
  641. def __init__(self):
  642. self.counters = [0] * 5
  643. self.values = [0] * 5
  644. see_int = func_with_new_name(_see, 'see_int')
  645. class ResumeGuardCountersRef(AbstractResumeGuardCounters):
  646. def __init__(self):
  647. self.counters = [0] * 5
  648. self.values = [history.ConstPtr.value] * 5
  649. see_ref = func_with_new_name(_see, 'see_ref')
  650. class ResumeGuardCountersFloat(AbstractResumeGuardCounters):
  651. def __init__(self):
  652. self.counters = [0] * 5
  653. self.values = [longlong.ZEROF] * 5
  654. see_float = func_with_new_name(_see, 'see_float')
  655. class ResumeFromInterpDescr(ResumeDescr):
  656. def __init__(self, original_greenkey):
  657. self.original_greenkey = original_greenkey
  658. def compile_and_attach(self, metainterp, new_loop):
  659. # We managed to create a bridge going from the interpreter
  660. # to previously-compiled code. We keep 'new_loop', which is not
  661. # a loop at all but ends in a jump to the target loop. It starts
  662. # with completely unoptimized arguments, as in the interpreter.
  663. metainterp_sd = metainterp.staticdata
  664. jitdriver_sd = metainterp.jitdriver_sd
  665. redargs = new_loop.inputargs
  666. new_loop.original_jitcell_token = jitcell_token = make_jitcell_token(jitdriver_sd)
  667. send_loop_to_backend(self.original_greenkey, metainterp.jitdriver_sd,
  668. metainterp_sd, new_loop, "entry bridge")
  669. # send the new_loop to warmspot.py, to be called directly the next time
  670. jitdriver_sd.warmstate.attach_procedure_to_interp(
  671. self.original_greenkey, jitcell_token)
  672. metainterp_sd.stats.add_jitcell_token(jitcell_token)
  673. def compile_trace(metainterp, resumekey, start_resumedescr=None):
  674. """Try to compile a new bridge leading from the beginning of the history
  675. to some existing place.
  676. """
  677. from pypy.jit.metainterp.optimizeopt import optimize_trace
  678. # The history contains new operations to attach as the code for the
  679. # failure of 'resumekey.guard_op'.
  680. #
  681. # Attempt to use optimize_bridge(). This may return None in case
  682. # it does not work -- i.e. none of the existing old_loop_tokens match.
  683. new_trace = create_empty_loop(metainterp)
  684. new_trace.inputargs = inputargs = metainterp.history.inputargs[:]
  685. # clone ops, as optimize_bridge can mutate the ops
  686. new_trace.operations = [op.clone() for op in metainterp.history.operations]
  687. new_trace.start_resumedescr = start_resumedescr
  688. metainterp_sd = metainterp.staticdata
  689. state = metainterp.jitdriver_sd.warmstate
  690. if isinstance(resumekey, ResumeAtPositionDescr):
  691. inline_short_preamble = False
  692. else:
  693. inline_short_preamble = True
  694. try:
  695. optimize_trace(metainterp_sd, new_trace, state.enable_opts, inline_short_preamble)
  696. except InvalidLoop:
  697. debug_print("compile_new_bridge: got an InvalidLoop")
  698. # XXX I am fairly convinced that optimize_bridge cannot actually raise
  699. # InvalidLoop
  700. debug_print('InvalidLoop in compile_new_bridge')
  701. return None
  702. if new_trace.operations[-1].getopnum() != rop.LABEL:
  703. # We managed to create a bridge. Dispatch to resumekey to
  704. # know exactly what we must do (ResumeGuardDescr/ResumeFromInterpDescr)
  705. target_token = new_trace.operations[-1].getdescr()
  706. resumekey.compile_and_attach(metainterp, new_trace)
  707. record_loop_or_bridge(metainterp_sd, new_trace)
  708. return target_token
  709. else:
  710. metainterp.retrace_needed(new_trace)
  711. return None
  712. # ____________________________________________________________
  713. class PropagateExceptionDescr(AbstractFailDescr):
  714. def handle_fail(self, metainterp_sd, jitdriver_sd):
  715. cpu = metainterp_sd.cpu
  716. exception = cpu.grab_exc_value()
  717. assert exception, "PropagateExceptionDescr: no exception??"
  718. raise metainterp_sd.ExitFrameWithExceptionRef(cpu, exception)
  719. def compile_tmp_callback(cpu, jitdriver_sd, greenboxes, redargtypes,
  720. memory_manager=None):
  721. """Make a LoopToken that corresponds to assembler code that just
  722. calls back the interpreter. Used temporarily: a fully compiled
  723. version of the code may end up replacing it.
  724. """
  725. jitcell_token = make_jitcell_token(jitdriver_sd)
  726. nb_red_args = jitdriver_sd.num_red_args
  727. assert len(redargtypes) == nb_red_args
  728. inputargs = []
  729. for kind in redargtypes:
  730. if kind == history.INT: box = BoxInt()
  731. elif kind == history.REF: box = BoxPtr()
  732. elif kind == history.FLOAT: box = BoxFloat()
  733. else: raise AssertionError
  734. inputargs.append(box)
  735. k = jitdriver_sd.portal_runner_adr
  736. funcbox = history.ConstInt(heaptracker.adr2int(k))
  737. callargs = [funcbox] + greenboxes + inputargs
  738. #
  739. result_type = jitdriver_sd.result_type
  740. if result_type == history.INT:
  741. result = BoxInt()
  742. elif result_type == history.REF:
  743. result = BoxPtr()
  744. elif result_type == history.FLOAT:
  745. result = BoxFloat()
  746. elif result_type == history.VOID:
  747. result = None
  748. else:
  749. assert 0, "bad result_type"
  750. if result is not None:
  751. finishargs = [result]
  752. else:
  753. finishargs = []
  754. #
  755. jd = jitdriver_sd
  756. faildescr = PropagateExceptionDescr()
  757. operations = [
  758. ResOperation(rop.CALL, callargs, result, descr=jd.portal_calldescr),
  759. ResOperation(rop.GUARD_NO_EXCEPTION, [], None, descr=faildescr),
  760. ResOperation(rop.FINISH, finishargs, None, descr=jd.portal_finishtoken)
  761. ]
  762. operations[1].setfailargs([])
  763. operations = get_deep_immutable_oplist(operations)
  764. cpu.compile_loop(inputargs, operations, jitcell_token, log=False)
  765. if memory_manager is not None: # for tests
  766. memory_manager.keep_loop_alive(jitcell_token)
  767. return jitcell_token