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

/pypy/module/pypyjit/interp_resop.py

https://bitbucket.org/pypy/pypy/
Python | 397 lines | 385 code | 7 blank | 5 comment | 6 complexity | cce5e4628e99ec7e0ccf5a6beefc9ad9 MD5 | raw file
Possible License(s): AGPL-3.0, BSD-3-Clause, Apache-2.0
  1. from pypy.interpreter.typedef import (TypeDef, GetSetProperty,
  2. interp_attrproperty, interp_attrproperty_w)
  3. from pypy.interpreter.baseobjspace import W_Root
  4. from pypy.interpreter.gateway import unwrap_spec, interp2app
  5. from pypy.interpreter.pycode import PyCode
  6. from pypy.interpreter.error import oefmt
  7. from rpython.rtyper.lltypesystem import lltype
  8. from rpython.rtyper.annlowlevel import cast_base_ptr_to_instance, hlstr
  9. from rpython.rtyper.rclass import OBJECT
  10. #from rpython.jit.metainterp.resoperation import rop
  11. from rpython.rlib.nonconst import NonConstant
  12. from rpython.rlib.rarithmetic import r_uint
  13. from rpython.rlib import jit_hooks
  14. from rpython.rlib.jit import Counters
  15. from rpython.rlib.objectmodel import compute_unique_id
  16. from pypy.module.pypyjit.interp_jit import pypyjitdriver
  17. class Cache(object):
  18. in_recursion = False
  19. no = 0
  20. def __init__(self, space):
  21. self.w_compile_hook = space.w_None
  22. self.w_abort_hook = space.w_None
  23. self.w_trace_too_long_hook = space.w_None
  24. def getno(self):
  25. self.no += 1
  26. return self.no - 1
  27. def wrap_greenkey(space, jitdriver, greenkey, greenkey_repr):
  28. if greenkey is None:
  29. return space.w_None
  30. jitdriver_name = jitdriver.name
  31. if jitdriver_name == 'pypyjit':
  32. next_instr = greenkey[0].getint()
  33. is_being_profiled = greenkey[1].getint()
  34. ll_code = lltype.cast_opaque_ptr(lltype.Ptr(OBJECT),
  35. greenkey[2].getref_base())
  36. pycode = cast_base_ptr_to_instance(PyCode, ll_code)
  37. return space.newtuple([space.wrap(pycode), space.wrap(next_instr),
  38. space.newbool(bool(is_being_profiled))])
  39. else:
  40. return space.wrap(greenkey_repr)
  41. @unwrap_spec(operations=bool)
  42. def set_compile_hook(space, w_hook, operations=True):
  43. """ set_compile_hook(hook, operations=True)
  44. Set a compiling hook that will be called each time a loop is compiled.
  45. The hook will be called with the pypyjit.JitLoopInfo object. Refer to it's
  46. docstring for details.
  47. Note that jit hook is not reentrant. It means that if the code
  48. inside the jit hook is itself jitted, it will get compiled, but the
  49. jit hook won't be called for that.
  50. """
  51. cache = space.fromcache(Cache)
  52. assert w_hook is not None
  53. cache.w_compile_hook = w_hook
  54. cache.compile_hook_with_ops = operations
  55. cache.in_recursion = NonConstant(False)
  56. def set_abort_hook(space, w_hook):
  57. """ set_abort_hook(hook)
  58. Set a hook (callable) that will be called each time there is tracing
  59. aborted due to some reason.
  60. The hook will be called with the signature:
  61. hook(jitdriver_name, greenkey, reason, operations)
  62. Reason is a string, the meaning of other arguments is the same
  63. as attributes on JitLoopInfo object.
  64. """
  65. cache = space.fromcache(Cache)
  66. assert w_hook is not None
  67. cache.w_abort_hook = w_hook
  68. cache.in_recursion = NonConstant(False)
  69. def set_trace_too_long_hook(space, w_hook):
  70. """ set_trace_too_long_hook(hook)
  71. Set a hook (callable) that will be called each time we abort
  72. tracing because the trace is too long.
  73. The hook will be called with the signature:
  74. hook(jitdriver_name, greenkey)
  75. """
  76. cache = space.fromcache(Cache)
  77. assert w_hook is not None
  78. cache.w_trace_too_long_hook = w_hook
  79. cache.in_recursion = NonConstant(False)
  80. def wrap_oplist(space, logops, operations, ops_offset=None):
  81. # this function is called from the JIT
  82. from rpython.jit.metainterp.resoperation import rop
  83. l_w = []
  84. jitdrivers_sd = logops.metainterp_sd.jitdrivers_sd
  85. for op in operations:
  86. if ops_offset is None:
  87. ofs = -1
  88. else:
  89. ofs = ops_offset.get(op, 0)
  90. num = op.getopnum()
  91. name = op.getopname()
  92. if num == rop.DEBUG_MERGE_POINT:
  93. jd_sd = jitdrivers_sd[op.getarg(0).getint()]
  94. greenkey = op.getarglist()[3:]
  95. repr = jd_sd.warmstate.get_location_str(greenkey)
  96. w_greenkey = wrap_greenkey(space, jd_sd.jitdriver, greenkey, repr)
  97. l_w.append(DebugMergePoint(space, name,
  98. logops.repr_of_resop(op),
  99. jd_sd.jitdriver.name,
  100. op.getarg(1).getint(),
  101. op.getarg(2).getint(),
  102. w_greenkey))
  103. elif op.is_guard():
  104. l_w.append(GuardOp(name, ofs, logops.repr_of_resop(op),
  105. op.getdescr().get_jitcounter_hash()))
  106. else:
  107. l_w.append(WrappedOp(name, ofs, logops.repr_of_resop(op)))
  108. return l_w
  109. @unwrap_spec(offset=int, repr=str, name=str)
  110. def descr_new_resop(space, w_tp, name, offset=-1, repr=''):
  111. return WrappedOp(name, offset, repr)
  112. @unwrap_spec(offset=int, repr=str, name=str, hash=r_uint)
  113. def descr_new_guardop(space, w_tp, name, offset=-1, repr='', hash=r_uint(0)):
  114. return GuardOp(name, offset, repr, hash)
  115. @unwrap_spec(repr=str, name=str, jd_name=str, call_depth=int, call_id=int)
  116. def descr_new_dmp(space, w_tp, name, repr, jd_name, call_depth, call_id,
  117. w_greenkey):
  118. return DebugMergePoint(space, name,
  119. repr, jd_name, call_depth, call_id, w_greenkey)
  120. class WrappedOp(W_Root):
  121. """ A class representing a single ResOperation, wrapped nicely
  122. """
  123. def __init__(self, name, offset, repr_of_resop):
  124. self.offset = offset
  125. self.name = name
  126. self.repr_of_resop = repr_of_resop
  127. def descr_repr(self, space):
  128. return space.wrap(self.repr_of_resop)
  129. def descr_name(self, space):
  130. return space.wrap(self.name)
  131. class GuardOp(WrappedOp):
  132. def __init__(self, name, offset, repr_of_resop, hash):
  133. WrappedOp.__init__(self, name, offset, repr_of_resop)
  134. self.hash = hash
  135. class DebugMergePoint(WrappedOp):
  136. """ A class representing Debug Merge Point - the entry point
  137. to a jitted loop.
  138. """
  139. def __init__(self, space, name, repr_of_resop, jd_name, call_depth,
  140. call_id, w_greenkey):
  141. WrappedOp.__init__(self, name, -1, repr_of_resop)
  142. self.jd_name = jd_name
  143. self.call_depth = call_depth
  144. self.call_id = call_id
  145. self.w_greenkey = w_greenkey
  146. def get_pycode(self, space):
  147. if self.jd_name == pypyjitdriver.name:
  148. return space.getitem(self.w_greenkey, space.wrap(0))
  149. raise oefmt(space.w_AttributeError,
  150. "This DebugMergePoint doesn't belong to the main Python "
  151. "JitDriver")
  152. def get_bytecode_no(self, space):
  153. if self.jd_name == pypyjitdriver.name:
  154. return space.getitem(self.w_greenkey, space.wrap(1))
  155. raise oefmt(space.w_AttributeError,
  156. "This DebugMergePoint doesn't belong to the main Python "
  157. "JitDriver")
  158. def get_jitdriver_name(self, space):
  159. return space.wrap(self.jd_name)
  160. WrappedOp.typedef = TypeDef(
  161. 'ResOperation',
  162. __doc__ = WrappedOp.__doc__,
  163. __new__ = interp2app(descr_new_resop),
  164. __repr__ = interp2app(WrappedOp.descr_repr),
  165. name = GetSetProperty(WrappedOp.descr_name),
  166. offset = interp_attrproperty("offset", cls=WrappedOp),
  167. )
  168. WrappedOp.typedef.acceptable_as_base_class = False
  169. GuardOp.typedef = TypeDef(
  170. 'GuardOp',
  171. __doc__ = GuardOp.__doc__,
  172. __new__ = interp2app(descr_new_guardop),
  173. __repr__ = interp2app(GuardOp.descr_repr),
  174. name = GetSetProperty(GuardOp.descr_name),
  175. offset = interp_attrproperty("offset", cls=GuardOp),
  176. hash = interp_attrproperty("hash", cls=GuardOp),
  177. )
  178. GuardOp.typedef.acceptable_as_base_class = False
  179. DebugMergePoint.typedef = TypeDef(
  180. 'DebugMergePoint', WrappedOp.typedef,
  181. __new__ = interp2app(descr_new_dmp),
  182. __doc__ = DebugMergePoint.__doc__,
  183. greenkey = interp_attrproperty_w("w_greenkey", cls=DebugMergePoint,
  184. doc="Representation of place where the loop was compiled. "
  185. "In the case of the main interpreter loop, it's a triplet "
  186. "(code, ofs, is_profiled)"),
  187. pycode = GetSetProperty(DebugMergePoint.get_pycode),
  188. bytecode_no = GetSetProperty(DebugMergePoint.get_bytecode_no,
  189. doc="offset in the bytecode"),
  190. call_depth = interp_attrproperty("call_depth", cls=DebugMergePoint,
  191. doc="Depth of calls within this loop"),
  192. call_id = interp_attrproperty("call_id", cls=DebugMergePoint,
  193. doc="Number of applevel function traced in this loop"),
  194. jitdriver_name = GetSetProperty(DebugMergePoint.get_jitdriver_name,
  195. doc="Name of the jitdriver 'pypyjit' in the case "
  196. "of the main interpreter loop"),
  197. )
  198. DebugMergePoint.typedef.acceptable_as_base_class = False
  199. class W_JitLoopInfo(W_Root):
  200. """ Loop debug information
  201. """
  202. w_green_key = None
  203. bridge_no = 0
  204. asmaddr = 0
  205. asmlen = 0
  206. def __init__(self, space, debug_info, is_bridge=False, wrap_ops=True):
  207. if wrap_ops:
  208. memo = {}
  209. logops = debug_info.logger._make_log_operations(memo)
  210. if debug_info.asminfo is not None:
  211. ofs = debug_info.asminfo.ops_offset
  212. else:
  213. ofs = {}
  214. ops = debug_info.operations
  215. self.w_ops = space.newlist(wrap_oplist(space, logops, ops, ofs))
  216. else:
  217. self.w_ops = space.w_None
  218. self.jd_name = debug_info.get_jitdriver().name
  219. self.type = debug_info.type
  220. if is_bridge:
  221. self.bridge_no = compute_unique_id(debug_info.fail_descr)
  222. #self.bridge_no = debug_info.fail_descr_no
  223. self.w_green_key = space.w_None
  224. else:
  225. self.w_green_key = wrap_greenkey(space,
  226. debug_info.get_jitdriver(),
  227. debug_info.greenkey,
  228. debug_info.get_greenkey_repr())
  229. self.loop_no = debug_info.looptoken.number
  230. asminfo = debug_info.asminfo
  231. if asminfo is not None:
  232. self.asmaddr = asminfo.asmaddr
  233. self.asmlen = asminfo.asmlen
  234. def descr_repr(self, space):
  235. lgt = space.int_w(space.len(self.w_ops))
  236. if self.type == "bridge":
  237. code_repr = 'bridge no %d' % self.bridge_no
  238. else:
  239. code_repr = space.str_w(space.repr(self.w_green_key))
  240. return space.wrap('<JitLoopInfo %s, %d operations, starting at <%s>>' %
  241. (self.jd_name, lgt, code_repr))
  242. def descr_get_bridge_no(self, space):
  243. if space.is_none(self.w_green_key):
  244. return space.wrap(self.bridge_no)
  245. raise oefmt(space.w_TypeError, "not a bridge")
  246. @unwrap_spec(loopno=int, asmaddr=int, asmlen=int, loop_no=int,
  247. type=str, jd_name=str, bridge_no=int)
  248. def descr_new_jit_loop_info(space, w_subtype, w_greenkey, w_ops, loopno,
  249. asmaddr, asmlen, loop_no, type, jd_name,
  250. bridge_no=-1):
  251. w_info = space.allocate_instance(W_JitLoopInfo, w_subtype)
  252. w_info.w_green_key = w_greenkey
  253. w_info.w_ops = w_ops
  254. w_info.asmaddr = asmaddr
  255. w_info.asmlen = asmlen
  256. w_info.loop_no = loop_no
  257. w_info.type = type
  258. w_info.jd_name = jd_name
  259. w_info.bridge_no = bridge_no
  260. return w_info
  261. W_JitLoopInfo.typedef = TypeDef(
  262. 'JitLoopInfo',
  263. __doc__ = W_JitLoopInfo.__doc__,
  264. __new__ = interp2app(descr_new_jit_loop_info),
  265. jitdriver_name = interp_attrproperty('jd_name', cls=W_JitLoopInfo,
  266. doc="Name of the JitDriver, pypyjit for the main one"),
  267. greenkey = interp_attrproperty_w('w_green_key', cls=W_JitLoopInfo,
  268. doc="Representation of place where the loop was compiled. "
  269. "In the case of the main interpreter loop, it's a triplet "
  270. "(code, ofs, is_profiled)"),
  271. operations = interp_attrproperty_w('w_ops', cls=W_JitLoopInfo, doc=
  272. "List of operations in this loop."),
  273. loop_no = interp_attrproperty('loop_no', cls=W_JitLoopInfo, doc=
  274. "Loop cardinal number"),
  275. bridge_no = GetSetProperty(W_JitLoopInfo.descr_get_bridge_no,
  276. doc="bridge number (if a bridge)"),
  277. type = interp_attrproperty('type', cls=W_JitLoopInfo,
  278. doc="Loop type"),
  279. asmaddr = interp_attrproperty('asmaddr', cls=W_JitLoopInfo,
  280. doc="Address of machine code"),
  281. asmlen = interp_attrproperty('asmlen', cls=W_JitLoopInfo,
  282. doc="Length of machine code"),
  283. __repr__ = interp2app(W_JitLoopInfo.descr_repr),
  284. )
  285. W_JitLoopInfo.typedef.acceptable_as_base_class = False
  286. class W_JitInfoSnapshot(W_Root):
  287. def __init__(self, space, w_times, w_counters, w_counter_times):
  288. self.w_loop_run_times = w_times
  289. self.w_counters = w_counters
  290. self.w_counter_times = w_counter_times
  291. W_JitInfoSnapshot.typedef = TypeDef(
  292. "JitInfoSnapshot",
  293. loop_run_times = interp_attrproperty_w("w_loop_run_times",
  294. cls=W_JitInfoSnapshot),
  295. counters = interp_attrproperty_w("w_counters",
  296. cls=W_JitInfoSnapshot,
  297. doc="various JIT counters"),
  298. counter_times = interp_attrproperty_w("w_counter_times",
  299. cls=W_JitInfoSnapshot,
  300. doc="various JIT timers")
  301. )
  302. W_JitInfoSnapshot.typedef.acceptable_as_base_class = False
  303. def get_stats_snapshot(space):
  304. """ Get the jit status in the specific moment in time. Note that this
  305. is eager - the attribute access is not lazy, if you need new stats
  306. you need to call this function again.
  307. """
  308. ll_times = jit_hooks.stats_get_loop_run_times(None)
  309. w_times = space.newdict()
  310. if ll_times:
  311. for i in range(len(ll_times)):
  312. w_key = space.newtuple([space.wrap(ll_times[i].type),
  313. space.wrap(ll_times[i].number)])
  314. space.setitem(w_times, w_key,
  315. space.wrap(ll_times[i].counter))
  316. w_counters = space.newdict()
  317. for i, counter_name in enumerate(Counters.counter_names):
  318. v = jit_hooks.stats_get_counter_value(None, i)
  319. space.setitem_str(w_counters, counter_name, space.wrap(v))
  320. w_counter_times = space.newdict()
  321. tr_time = jit_hooks.stats_get_times_value(None, Counters.TRACING)
  322. space.setitem_str(w_counter_times, 'TRACING', space.wrap(tr_time))
  323. b_time = jit_hooks.stats_get_times_value(None, Counters.BACKEND)
  324. space.setitem_str(w_counter_times, 'BACKEND', space.wrap(b_time))
  325. return space.wrap(W_JitInfoSnapshot(space, w_times, w_counters,
  326. w_counter_times))
  327. def get_stats_asmmemmgr(space):
  328. """Returns the raw memory currently used by the JIT backend,
  329. as a pair (total_memory_allocated, memory_in_use)."""
  330. m1 = jit_hooks.stats_asmmemmgr_allocated(None)
  331. m2 = jit_hooks.stats_asmmemmgr_used(None)
  332. return space.newtuple([space.wrap(m1), space.wrap(m2)])
  333. def enable_debug(space):
  334. """ Set the jit debugging - completely necessary for some stats to work,
  335. most notably assembler counters.
  336. """
  337. jit_hooks.stats_set_debug(None, True)
  338. def disable_debug(space):
  339. """ Disable the jit debugging. This means some very small loops will be
  340. marginally faster and the counters will stop working.
  341. """
  342. jit_hooks.stats_set_debug(None, False)