PageRenderTime 114ms CodeModel.GetById 1ms RepoModel.GetById 0ms app.codeStats 0ms

/pypy/module/pypyjit/interp_jit.py

https://bitbucket.org/pypy/pypy/
Python | 307 lines | 296 code | 7 blank | 4 comment | 0 complexity | 47558d529c1cf1ac14f534da417cad2a MD5 | raw file
Possible License(s): AGPL-3.0, BSD-3-Clause, Apache-2.0
  1. """This is not the JIT :-)
  2. This is transformed to become a JIT by code elsewhere: rpython/jit/*
  3. """
  4. from rpython.rlib.rarithmetic import r_uint, intmask
  5. from rpython.rlib.jit import JitDriver, hint, we_are_jitted, dont_look_inside
  6. from rpython.rlib import jit, jit_hooks
  7. from rpython.rlib.rjitlog import rjitlog as jl
  8. from rpython.rlib.jit import current_trace_length, unroll_parameters,\
  9. JitHookInterface
  10. from rpython.rtyper.annlowlevel import cast_instance_to_gcref
  11. import pypy.interpreter.pyopcode # for side-effects
  12. from pypy.interpreter.error import OperationError, oefmt
  13. from pypy.interpreter.pycode import CO_GENERATOR, PyCode
  14. from pypy.interpreter.gateway import unwrap_spec
  15. from pypy.interpreter.pyframe import PyFrame
  16. from pypy.interpreter.pyopcode import ExitFrame, Yield
  17. from pypy.interpreter.baseobjspace import W_Root
  18. from pypy.interpreter.typedef import TypeDef
  19. from pypy.interpreter.gateway import interp2app
  20. from opcode import opmap
  21. PyFrame._virtualizable_ = ['last_instr', 'pycode',
  22. 'valuestackdepth',
  23. 'locals_cells_stack_w[*]',
  24. 'debugdata',
  25. 'last_exception',
  26. 'lastblock',
  27. 'w_globals',
  28. ]
  29. JUMP_ABSOLUTE = opmap['JUMP_ABSOLUTE']
  30. def get_printable_location(next_instr, is_being_profiled, bytecode):
  31. from pypy.tool.stdlib_opcode import opcode_method_names
  32. name = opcode_method_names[ord(bytecode.co_code[next_instr])]
  33. return '%s #%d %s' % (bytecode.get_repr(), next_instr, name)
  34. def get_unique_id(next_instr, is_being_profiled, bytecode):
  35. from rpython.rlib import rvmprof
  36. return rvmprof.get_unique_id(bytecode)
  37. @jl.returns(jl.MP_FILENAME, jl.MP_LINENO,
  38. jl.MP_SCOPE, jl.MP_INDEX, jl.MP_OPCODE)
  39. def get_location(next_instr, is_being_profiled, bytecode):
  40. from pypy.tool.stdlib_opcode import opcode_method_names
  41. from rpython.tool.error import offset2lineno
  42. bcindex = ord(bytecode.co_code[next_instr])
  43. opname = ""
  44. if 0 <= bcindex < len(opcode_method_names):
  45. opname = opcode_method_names[bcindex]
  46. name = bytecode.co_name
  47. if not name:
  48. name = ""
  49. line = offset2lineno(bytecode, intmask(next_instr))
  50. return (bytecode.co_filename, line,
  51. name, intmask(next_instr), opname)
  52. def should_unroll_one_iteration(next_instr, is_being_profiled, bytecode):
  53. return (bytecode.co_flags & CO_GENERATOR) != 0
  54. class PyPyJitDriver(JitDriver):
  55. reds = ['frame', 'ec']
  56. greens = ['next_instr', 'is_being_profiled', 'pycode']
  57. virtualizables = ['frame']
  58. pypyjitdriver = PyPyJitDriver(get_printable_location = get_printable_location,
  59. get_location = get_location,
  60. get_unique_id = get_unique_id,
  61. should_unroll_one_iteration =
  62. should_unroll_one_iteration,
  63. name='pypyjit',
  64. is_recursive=True)
  65. class __extend__(PyFrame):
  66. def dispatch(self, pycode, next_instr, ec):
  67. self = hint(self, access_directly=True)
  68. next_instr = r_uint(next_instr)
  69. is_being_profiled = self.get_is_being_profiled()
  70. try:
  71. while True:
  72. pypyjitdriver.jit_merge_point(ec=ec,
  73. frame=self, next_instr=next_instr, pycode=pycode,
  74. is_being_profiled=is_being_profiled)
  75. co_code = pycode.co_code
  76. self.valuestackdepth = hint(self.valuestackdepth, promote=True)
  77. next_instr = self.handle_bytecode(co_code, next_instr, ec)
  78. is_being_profiled = self.get_is_being_profiled()
  79. except Yield:
  80. self.last_exception = None
  81. w_result = self.popvalue()
  82. jit.hint(self, force_virtualizable=True)
  83. return w_result
  84. except ExitFrame:
  85. self.last_exception = None
  86. return self.popvalue()
  87. def jump_absolute(self, jumpto, ec):
  88. if we_are_jitted():
  89. #
  90. # assume that only threads are using the bytecode counter
  91. decr_by = 0
  92. if self.space.actionflag.has_bytecode_counter: # constant-folded
  93. if self.space.threadlocals.gil_ready: # quasi-immutable field
  94. decr_by = _get_adapted_tick_counter()
  95. #
  96. self.last_instr = intmask(jumpto)
  97. ec.bytecode_trace(self, decr_by)
  98. jumpto = r_uint(self.last_instr)
  99. #
  100. pypyjitdriver.can_enter_jit(frame=self, ec=ec, next_instr=jumpto,
  101. pycode=self.getcode(),
  102. is_being_profiled=self.get_is_being_profiled())
  103. return jumpto
  104. def _get_adapted_tick_counter():
  105. # Normally, the tick counter is decremented by 100 for every
  106. # Python opcode. Here, to better support JIT compilation of
  107. # small loops, we decrement it by a possibly smaller constant.
  108. # We get the maximum 100 when the (unoptimized) trace length
  109. # is at least 3200 (a bit randomly).
  110. trace_length = r_uint(current_trace_length())
  111. decr_by = trace_length // 32
  112. if decr_by < 1:
  113. decr_by = 1
  114. elif decr_by > 100: # also if current_trace_length() returned -1
  115. decr_by = 100
  116. return intmask(decr_by)
  117. # ____________________________________________________________
  118. #
  119. # Public interface
  120. def set_param(space, __args__):
  121. '''Configure the tunable JIT parameters.
  122. * set_param(name=value, ...) # as keyword arguments
  123. * set_param("name=value,name=value") # as a user-supplied string
  124. * set_param("off") # disable the jit
  125. * set_param("default") # restore all defaults
  126. '''
  127. # XXXXXXXXX
  128. args_w, kwds_w = __args__.unpack()
  129. if len(args_w) > 1:
  130. raise oefmt(space.w_TypeError,
  131. "set_param() takes at most 1 non-keyword argument, %d "
  132. "given", len(args_w))
  133. if len(args_w) == 1:
  134. text = space.str_w(args_w[0])
  135. try:
  136. jit.set_user_param(None, text)
  137. except ValueError:
  138. raise oefmt(space.w_ValueError, "error in JIT parameters string")
  139. for key, w_value in kwds_w.items():
  140. if key == 'enable_opts':
  141. jit.set_param(None, 'enable_opts', space.str_w(w_value))
  142. else:
  143. intval = space.int_w(w_value)
  144. for name, _ in unroll_parameters:
  145. if name == key and name != 'enable_opts':
  146. jit.set_param(None, name, intval)
  147. break
  148. else:
  149. raise oefmt(space.w_TypeError, "no JIT parameter '%s'", key)
  150. @dont_look_inside
  151. def residual_call(space, w_callable, __args__):
  152. '''For testing. Invokes callable(...), but without letting
  153. the JIT follow the call.'''
  154. return space.call_args(w_callable, __args__)
  155. class W_NotFromAssembler(W_Root):
  156. def __init__(self, space, w_callable):
  157. self.space = space
  158. self.w_callable = w_callable
  159. def descr_call(self, __args__):
  160. _call_not_in_trace(self.space, self.w_callable, __args__)
  161. return self
  162. @jit.not_in_trace
  163. def _call_not_in_trace(space, w_callable, __args__):
  164. # this _call_not_in_trace() must return None
  165. space.call_args(w_callable, __args__)
  166. def not_from_assembler_new(space, w_subtype, w_callable):
  167. return W_NotFromAssembler(space, w_callable)
  168. W_NotFromAssembler.typedef = TypeDef("not_from_assembler",
  169. __doc__ = """\
  170. A decorator that returns a callable that invokes the original
  171. callable, but not from the JIT-produced assembler. It is called
  172. from the interpreted mode, and from the JIT creation (pyjitpl) or
  173. exiting (blackhole) steps, but just not from the final assembler.
  174. Note that the return value of the callable is ignored, because
  175. there is no reasonable way to guess what it should be in case the
  176. function is not called.
  177. This is meant to be used notably in sys.settrace() for coverage-
  178. like tools. For that purpose, if g = not_from_assembler(f), then
  179. 'g(*args)' may call 'f(*args)' but it always return g itself.
  180. """,
  181. __new__ = interp2app(not_from_assembler_new),
  182. __call__ = interp2app(W_NotFromAssembler.descr_call),
  183. )
  184. W_NotFromAssembler.typedef.acceptable_as_base_class = False
  185. @unwrap_spec(next_instr=int, is_being_profiled=bool, w_pycode=PyCode)
  186. @dont_look_inside
  187. def get_jitcell_at_key(space, next_instr, is_being_profiled, w_pycode):
  188. ll_pycode = cast_instance_to_gcref(w_pycode)
  189. return space.wrap(bool(jit_hooks.get_jitcell_at_key(
  190. 'pypyjit', r_uint(next_instr), int(is_being_profiled), ll_pycode)))
  191. @unwrap_spec(next_instr=int, is_being_profiled=bool, w_pycode=PyCode)
  192. @dont_look_inside
  193. def dont_trace_here(space, next_instr, is_being_profiled, w_pycode):
  194. ll_pycode = cast_instance_to_gcref(w_pycode)
  195. jit_hooks.dont_trace_here(
  196. 'pypyjit', r_uint(next_instr), int(is_being_profiled), ll_pycode)
  197. return space.w_None
  198. @unwrap_spec(next_instr=int, is_being_profiled=bool, w_pycode=PyCode)
  199. @dont_look_inside
  200. def trace_next_iteration(space, next_instr, is_being_profiled, w_pycode):
  201. ll_pycode = cast_instance_to_gcref(w_pycode)
  202. jit_hooks.trace_next_iteration(
  203. 'pypyjit', r_uint(next_instr), int(is_being_profiled), ll_pycode)
  204. return space.w_None
  205. @unwrap_spec(hash=r_uint)
  206. @dont_look_inside
  207. def trace_next_iteration_hash(space, hash):
  208. jit_hooks.trace_next_iteration_hash('pypyjit', hash)
  209. return space.w_None
  210. # class Cache(object):
  211. # in_recursion = False
  212. # def __init__(self, space):
  213. # self.w_compile_bridge = space.w_None
  214. # self.w_compile_loop = space.w_None
  215. # def set_compile_bridge(space, w_hook):
  216. # cache = space.fromcache(Cache)
  217. # assert w_hook is not None
  218. # cache.w_compile_bridge = w_hook
  219. # def set_compile_loop(space, w_hook):
  220. # from rpython.rlib.nonconst import NonConstant
  221. # cache = space.fromcache(Cache)
  222. # assert w_hook is not None
  223. # cache.w_compile_loop = w_hook
  224. # cache.in_recursion = NonConstant(False)
  225. # class PyPyJitHookInterface(JitHookInterface):
  226. # def after_compile(self, debug_info):
  227. # space = self.space
  228. # cache = space.fromcache(Cache)
  229. # if cache.in_recursion:
  230. # return
  231. # l_w = []
  232. # if not space.is_true(cache.w_compile_loop):
  233. # return
  234. # for i, op in enumerate(debug_info.operations):
  235. # if op.is_guard():
  236. # w_t = space.newtuple([space.wrap(i), space.wrap(op.getopnum()), space.wrap(op.getdescr().get_jitcounter_hash())])
  237. # l_w.append(w_t)
  238. # try:
  239. # cache.in_recursion = True
  240. # try:
  241. # space.call_function(cache.w_compile_loop, space.newlist(l_w))
  242. # except OperationError, e:
  243. # e.write_unraisable(space, "jit hook ", cache.w_compile_bridge)
  244. # finally:
  245. # cache.in_recursion = False
  246. # def after_compile_bridge(self, debug_info):
  247. # space = self.space
  248. # cache = space.fromcache(Cache)
  249. # if cache.in_recursion:
  250. # return
  251. # if not space.is_true(cache.w_compile_bridge):
  252. # return
  253. # w_hash = space.wrap(debug_info.fail_descr.get_jitcounter_hash())
  254. # try:
  255. # cache.in_recursion = True
  256. # try:
  257. # space.call_function(cache.w_compile_bridge, w_hash)
  258. # except OperationError, e:
  259. # e.write_unraisable(space, "jit hook ", cache.w_compile_bridge)
  260. # finally:
  261. # cache.in_recursion = False
  262. # def before_compile(self, debug_info):
  263. # pass
  264. # def before_compile_bridge(self, debug_info):
  265. # pass
  266. # pypy_hooks = PyPyJitHookInterface()