pypy /pypy/module/pypyjit/interp_jit.py

Language Python Lines 308
MD5 Hash 47558d529c1cf1ac14f534da417cad2a Estimated Cost $4,750 (why?)
Repository https://bitbucket.org/pypy/pypy/ View Raw File View Project SPDX
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
"""This is not the JIT :-)

This is transformed to become a JIT by code elsewhere: rpython/jit/*
"""

from rpython.rlib.rarithmetic import r_uint, intmask
from rpython.rlib.jit import JitDriver, hint, we_are_jitted, dont_look_inside
from rpython.rlib import jit, jit_hooks
from rpython.rlib.rjitlog import rjitlog as jl
from rpython.rlib.jit import current_trace_length, unroll_parameters,\
     JitHookInterface
from rpython.rtyper.annlowlevel import cast_instance_to_gcref
import pypy.interpreter.pyopcode   # for side-effects
from pypy.interpreter.error import OperationError, oefmt
from pypy.interpreter.pycode import CO_GENERATOR, PyCode
from pypy.interpreter.gateway import unwrap_spec
from pypy.interpreter.pyframe import PyFrame
from pypy.interpreter.pyopcode import ExitFrame, Yield
from pypy.interpreter.baseobjspace import W_Root
from pypy.interpreter.typedef import TypeDef
from pypy.interpreter.gateway import interp2app
from opcode import opmap


PyFrame._virtualizable_ = ['last_instr', 'pycode',
                           'valuestackdepth',
                           'locals_cells_stack_w[*]',
                           'debugdata',
                           'last_exception',
                           'lastblock',
                           'w_globals',
                           ]

JUMP_ABSOLUTE = opmap['JUMP_ABSOLUTE']

def get_printable_location(next_instr, is_being_profiled, bytecode):
    from pypy.tool.stdlib_opcode import opcode_method_names
    name = opcode_method_names[ord(bytecode.co_code[next_instr])]
    return '%s #%d %s' % (bytecode.get_repr(), next_instr, name)

def get_unique_id(next_instr, is_being_profiled, bytecode):
    from rpython.rlib import rvmprof
    return rvmprof.get_unique_id(bytecode)

@jl.returns(jl.MP_FILENAME, jl.MP_LINENO,
            jl.MP_SCOPE, jl.MP_INDEX, jl.MP_OPCODE)
def get_location(next_instr, is_being_profiled, bytecode):
    from pypy.tool.stdlib_opcode import opcode_method_names
    from rpython.tool.error import offset2lineno
    bcindex = ord(bytecode.co_code[next_instr])
    opname = ""
    if 0 <= bcindex < len(opcode_method_names):
        opname = opcode_method_names[bcindex]
    name = bytecode.co_name
    if not name:
        name = ""
    line = offset2lineno(bytecode, intmask(next_instr))
    return (bytecode.co_filename, line,
            name, intmask(next_instr), opname)

def should_unroll_one_iteration(next_instr, is_being_profiled, bytecode):
    return (bytecode.co_flags & CO_GENERATOR) != 0

class PyPyJitDriver(JitDriver):
    reds = ['frame', 'ec']
    greens = ['next_instr', 'is_being_profiled', 'pycode']
    virtualizables = ['frame']

pypyjitdriver = PyPyJitDriver(get_printable_location = get_printable_location,
                              get_location = get_location,
                              get_unique_id = get_unique_id,
                              should_unroll_one_iteration =
                              should_unroll_one_iteration,
                              name='pypyjit',
                              is_recursive=True)

class __extend__(PyFrame):

    def dispatch(self, pycode, next_instr, ec):
        self = hint(self, access_directly=True)
        next_instr = r_uint(next_instr)
        is_being_profiled = self.get_is_being_profiled()
        try:
            while True:
                pypyjitdriver.jit_merge_point(ec=ec,
                    frame=self, next_instr=next_instr, pycode=pycode,
                    is_being_profiled=is_being_profiled)
                co_code = pycode.co_code
                self.valuestackdepth = hint(self.valuestackdepth, promote=True)
                next_instr = self.handle_bytecode(co_code, next_instr, ec)
                is_being_profiled = self.get_is_being_profiled()
        except Yield:
            self.last_exception = None
            w_result = self.popvalue()
            jit.hint(self, force_virtualizable=True)
            return w_result
        except ExitFrame:
            self.last_exception = None
            return self.popvalue()

    def jump_absolute(self, jumpto, ec):
        if we_are_jitted():
            #
            # assume that only threads are using the bytecode counter
            decr_by = 0
            if self.space.actionflag.has_bytecode_counter:   # constant-folded
                if self.space.threadlocals.gil_ready:   # quasi-immutable field
                    decr_by = _get_adapted_tick_counter()
            #
            self.last_instr = intmask(jumpto)
            ec.bytecode_trace(self, decr_by)
            jumpto = r_uint(self.last_instr)
        #
        pypyjitdriver.can_enter_jit(frame=self, ec=ec, next_instr=jumpto,
                                 pycode=self.getcode(),
                                 is_being_profiled=self.get_is_being_profiled())
        return jumpto

def _get_adapted_tick_counter():
    # Normally, the tick counter is decremented by 100 for every
    # Python opcode.  Here, to better support JIT compilation of
    # small loops, we decrement it by a possibly smaller constant.
    # We get the maximum 100 when the (unoptimized) trace length
    # is at least 3200 (a bit randomly).
    trace_length = r_uint(current_trace_length())
    decr_by = trace_length // 32
    if decr_by < 1:
        decr_by = 1
    elif decr_by > 100:    # also if current_trace_length() returned -1
        decr_by = 100
    return intmask(decr_by)


# ____________________________________________________________
#
# Public interface

def set_param(space, __args__):
    '''Configure the tunable JIT parameters.
        * set_param(name=value, ...)            # as keyword arguments
        * set_param("name=value,name=value")    # as a user-supplied string
        * set_param("off")                      # disable the jit
        * set_param("default")                  # restore all defaults
    '''
    # XXXXXXXXX
    args_w, kwds_w = __args__.unpack()
    if len(args_w) > 1:
        raise oefmt(space.w_TypeError,
                    "set_param() takes at most 1 non-keyword argument, %d "
                    "given", len(args_w))
    if len(args_w) == 1:
        text = space.str_w(args_w[0])
        try:
            jit.set_user_param(None, text)
        except ValueError:
            raise oefmt(space.w_ValueError, "error in JIT parameters string")
    for key, w_value in kwds_w.items():
        if key == 'enable_opts':
            jit.set_param(None, 'enable_opts', space.str_w(w_value))
        else:
            intval = space.int_w(w_value)
            for name, _ in unroll_parameters:
                if name == key and name != 'enable_opts':
                    jit.set_param(None, name, intval)
                    break
            else:
                raise oefmt(space.w_TypeError, "no JIT parameter '%s'", key)

@dont_look_inside
def residual_call(space, w_callable, __args__):
    '''For testing.  Invokes callable(...), but without letting
    the JIT follow the call.'''
    return space.call_args(w_callable, __args__)


class W_NotFromAssembler(W_Root):
    def __init__(self, space, w_callable):
        self.space = space
        self.w_callable = w_callable
    def descr_call(self, __args__):
        _call_not_in_trace(self.space, self.w_callable, __args__)
        return self

@jit.not_in_trace
def _call_not_in_trace(space, w_callable, __args__):
    # this _call_not_in_trace() must return None
    space.call_args(w_callable, __args__)

def not_from_assembler_new(space, w_subtype, w_callable):
    return W_NotFromAssembler(space, w_callable)

W_NotFromAssembler.typedef = TypeDef("not_from_assembler",
    __doc__ = """\
A decorator that returns a callable that invokes the original
callable, but not from the JIT-produced assembler.  It is called
from the interpreted mode, and from the JIT creation (pyjitpl) or
exiting (blackhole) steps, but just not from the final assembler.

Note that the return value of the callable is ignored, because
there is no reasonable way to guess what it should be in case the
function is not called.

This is meant to be used notably in sys.settrace() for coverage-
like tools.  For that purpose, if g = not_from_assembler(f), then
'g(*args)' may call 'f(*args)' but it always return g itself.
""",
    __new__ = interp2app(not_from_assembler_new),
    __call__ = interp2app(W_NotFromAssembler.descr_call),
)
W_NotFromAssembler.typedef.acceptable_as_base_class = False

@unwrap_spec(next_instr=int, is_being_profiled=bool, w_pycode=PyCode)
@dont_look_inside
def get_jitcell_at_key(space, next_instr, is_being_profiled, w_pycode):
    ll_pycode = cast_instance_to_gcref(w_pycode)
    return space.wrap(bool(jit_hooks.get_jitcell_at_key(
        'pypyjit', r_uint(next_instr), int(is_being_profiled), ll_pycode)))

@unwrap_spec(next_instr=int, is_being_profiled=bool, w_pycode=PyCode)
@dont_look_inside
def dont_trace_here(space, next_instr, is_being_profiled, w_pycode):
    ll_pycode = cast_instance_to_gcref(w_pycode)
    jit_hooks.dont_trace_here(
        'pypyjit', r_uint(next_instr), int(is_being_profiled), ll_pycode)
    return space.w_None

@unwrap_spec(next_instr=int, is_being_profiled=bool, w_pycode=PyCode)
@dont_look_inside
def trace_next_iteration(space, next_instr, is_being_profiled, w_pycode):
    ll_pycode = cast_instance_to_gcref(w_pycode)
    jit_hooks.trace_next_iteration(
        'pypyjit', r_uint(next_instr), int(is_being_profiled), ll_pycode)
    return space.w_None

@unwrap_spec(hash=r_uint)
@dont_look_inside
def trace_next_iteration_hash(space, hash):
    jit_hooks.trace_next_iteration_hash('pypyjit', hash)
    return space.w_None

# class Cache(object):
#     in_recursion = False

#     def __init__(self, space):
#         self.w_compile_bridge = space.w_None
#         self.w_compile_loop = space.w_None

# def set_compile_bridge(space, w_hook):
#     cache = space.fromcache(Cache)
#     assert w_hook is not None
#     cache.w_compile_bridge = w_hook

# def set_compile_loop(space, w_hook):
#     from rpython.rlib.nonconst import NonConstant
    
#     cache = space.fromcache(Cache)
#     assert w_hook is not None
#     cache.w_compile_loop = w_hook
#     cache.in_recursion = NonConstant(False)

# class PyPyJitHookInterface(JitHookInterface):
#     def after_compile(self, debug_info):
#         space = self.space
#         cache = space.fromcache(Cache)
#         if cache.in_recursion:
#             return
#         l_w = []
#         if not space.is_true(cache.w_compile_loop):
#             return
#         for i, op in enumerate(debug_info.operations):
#             if op.is_guard():
#                 w_t = space.newtuple([space.wrap(i), space.wrap(op.getopnum()), space.wrap(op.getdescr().get_jitcounter_hash())])
#                 l_w.append(w_t)
#         try:
#             cache.in_recursion = True
#             try:
#                 space.call_function(cache.w_compile_loop, space.newlist(l_w))
#             except OperationError, e:
#                 e.write_unraisable(space, "jit hook ", cache.w_compile_bridge)
#         finally:
#             cache.in_recursion = False

#     def after_compile_bridge(self, debug_info):
#         space = self.space
#         cache = space.fromcache(Cache)
#         if cache.in_recursion:
#             return
#         if not space.is_true(cache.w_compile_bridge):
#             return
#         w_hash = space.wrap(debug_info.fail_descr.get_jitcounter_hash())
#         try:
#             cache.in_recursion = True
#             try:
#                 space.call_function(cache.w_compile_bridge, w_hash)
#             except OperationError, e:
#                 e.write_unraisable(space, "jit hook ", cache.w_compile_bridge)
#         finally:
#             cache.in_recursion = False

#     def before_compile(self, debug_info):
#         pass

#     def before_compile_bridge(self, debug_info):
#         pass

# pypy_hooks = PyPyJitHookInterface()
Back to Top