Commits

Antonio Cuni  committed 57e9e9d

merge r70415 and r70454 from trunk: merge the jit-trace branch and kill a lot
of "we_are_jitted", to make sys.settrace working inside loops

  • Participants
  • Parent commits af03642
  • Branches cli-jit

Comments (0)

Files changed (10)

File pypy/interpreter/baseobjspace.py

 from pypy.rlib.objectmodel import we_are_translated
 from pypy.rlib.debug import make_sure_not_resized
 from pypy.rlib.timer import DummyTimer, Timer
-from pypy.rlib.jit import we_are_jitted, dont_look_inside, unroll_safe
+from pypy.rlib.jit import unroll_safe
 import os, sys
 
 __all__ = ['ObjSpace', 'OperationError', 'Wrappable', 'W_Root']
 
     def call_valuestack(self, w_func, nargs, frame):
         from pypy.interpreter.function import Function, Method, is_builtin_code
-        if (not we_are_jitted() and frame.is_being_profiled and
-            is_builtin_code(w_func)):
+        if frame.is_being_profiled and is_builtin_code(w_func):
             # XXX: this code is copied&pasted :-( from the slow path below
             # call_valuestack().
             args = frame.make_arguments(nargs)
         args = frame.make_arguments(nargs)
         return self.call_args(w_func, args)
 
-    @dont_look_inside 
     def call_args_and_c_profile(self, frame, w_func, args):
         ec = self.getexecutioncontext()
         ec.c_call_trace(frame, w_func)

File pypy/interpreter/executioncontext.py

 from pypy.interpreter.error import OperationError
 from pypy.rlib.rarithmetic import LONG_BIT
 from pypy.rlib.unroll import unrolling_iterable
+from pypy.rlib import jit
 from pypy.rlib.jit import we_are_jitted
-from pypy.rlib import jit
 
 def app_profile_call(space, w_callable, frame, event, w_arg):
     space.call_function(w_callable,
     """An ExecutionContext holds the state of an execution thread
     in the Python interpreter."""
 
+    # XXX JIT: when tracing (but not when blackholing!), the following
+    # XXX fields should be known to a constant None or False:
+    # XXX   self.w_tracefunc, self.profilefunc
+    # XXX   frame.is_being_profiled
+
     def __init__(self, space):
         self.space = space
         self._init_frame_chain()
         # tracing: space.frame_trace_action.fire() must be called to ensure
         # that tracing occurs whenever self.w_tracefunc or self.is_tracing
         # is modified.
-        self.w_tracefunc = None
+        self.w_tracefunc = None        # if not None, no JIT
         self.is_tracing = 0
         self.compiler = space.createcompiler()
-        self.profilefunc = None
+        self.profilefunc = None        # if not None, no JIT
         self.w_profilefuncarg = None
 
     def gettopframe_nohidden(self):
     def leave(self, frame):
         if self.profilefunc:
             self._trace(frame, 'leaveframe', self.space.w_None)
-
         self._unchain(frame)
         
         if self.w_tracefunc is not None and not frame.hide():
         space.setitem(w_globals, w_key, w_value)
         return w_globals
 
-    @jit.dont_look_inside
     def c_call_trace(self, frame, w_func):
         "Profile the call of a builtin function"
         if self.profilefunc is None:
         else:
             self._trace(frame, 'c_call', w_func)
 
-    @jit.dont_look_inside
     def c_return_trace(self, frame, w_retval):
         "Profile the return from a builtin function"
         if self.profilefunc is None:
         else:
             self._trace(frame, 'c_return', w_retval)
 
-    @jit.dont_look_inside
     def c_exception_trace(self, frame, w_exc):
         "Profile function called upon OperationError."
         if self.profilefunc is None:
         else:
             self._trace(frame, 'c_exception', w_exc)
 
-    @jit.dont_look_inside
     def call_trace(self, frame):
         "Trace the call of a function"
         if self.w_tracefunc is not None or self.profilefunc is not None:
             if self.profilefunc:
                 frame.is_being_profiled = True
 
-    @jit.dont_look_inside
     def return_trace(self, frame, w_retval):
         "Trace the return from a function"
         if self.w_tracefunc is not None:
             actionflag.action_dispatcher(self, frame)     # slow path
     bytecode_trace._always_inline_ = True
 
-    @jit.dont_look_inside
+    def bytecode_trace_after_exception(self, frame):
+        "Like bytecode_trace(), but without increasing the ticker."
+        actionflag = self.space.actionflag
+        ticker = actionflag.get()
+        if ticker & actionflag.interesting_bits:  # fast check
+            actionflag.action_dispatcher(self, frame)     # slow path
+    bytecode_trace_after_exception._always_inline_ = True
+
     def exception_trace(self, frame, operationerr):
         "Trace function called upon OperationError."
         operationerr.record_interpreter_traceback()
         if self.space.is_w(w_func, self.space.w_None):
             self.w_tracefunc = None
         else:
+            self.force_all_frames()
             self.w_tracefunc = w_func
             self.space.frame_trace_action.fire()
 
             self.setllprofile(app_profile_call, w_func)
 
     def setllprofile(self, func, w_arg):
-        self.profilefunc = func
         if func is not None:
             if w_arg is None:
                 raise ValueError("Cannot call setllprofile with real None")
-            frame = self.gettopframe_nohidden()
-            while frame:
+            self.force_all_frames(is_being_profiled=True)
+        self.profilefunc = func
+        self.w_profilefuncarg = w_arg
+
+    def force_all_frames(self, is_being_profiled=False):
+        # "Force" all frames in the sense of the jit, and optionally
+        # set the flag 'is_being_profiled' on them.  A forced frame is
+        # one out of which the jit will exit: if it is running so far,
+        # in a piece of assembler currently running a CALL_MAY_FORCE,
+        # then being forced means that it will fail the following
+        # GUARD_NOT_FORCED operation, and so fall back to interpreted
+        # execution.
+        frame = self.gettopframe_nohidden()
+        while frame:
+            if is_being_profiled:
                 frame.is_being_profiled = True
-                frame = self.getnextframe_nohidden(frame)
-        self.w_profilefuncarg = w_arg
+            frame = self.getnextframe_nohidden(frame)
 
     def call_tracing(self, w_func, w_args):
         is_tracing = self.is_tracing
 
     def fire_after_thread_switch(self):
         """Bit of a hack: fire() the action but only the next time the GIL
-        is released and re-acquired (i.e. after a portential thread switch).
+        is released and re-acquired (i.e. after a potential thread switch).
         Don't call this if threads are not enabled.
         """
         from pypy.module.thread.gil import spacestate

File pypy/interpreter/pyframe.py

 from pypy.interpreter import pytraceback
 import opcode
 from pypy.rlib.objectmodel import we_are_translated, instantiate
-from pypy.rlib.jit import we_are_jitted, hint
+from pypy.rlib.jit import hint
 from pypy.rlib.debug import make_sure_not_resized
 from pypy.rlib import jit
 
         executioncontext = self.space.getexecutioncontext()
         executioncontext.enter(self)
         try:
-            if not we_are_jitted():
-                executioncontext.call_trace(self)
+            executioncontext.call_trace(self)
             # Execution starts just after the last_instr.  Initially,
             # last_instr is -1.  After a generator suspends it points to
             # the YIELD_VALUE instruction.
                 rstack.resume_point("execute_frame", self, executioncontext,
                                     returns=w_exitvalue)
             except Exception:
-                if not we_are_jitted():
-                    executioncontext.return_trace(self, self.space.w_None)
+                executioncontext.return_trace(self, self.space.w_None)
                 raise
-            if not we_are_jitted():
-                executioncontext.return_trace(self, w_exitvalue)
+            executioncontext.return_trace(self, w_exitvalue)
         finally:
             executioncontext.leave(self)
         return w_exitvalue
         new_frame.instr_prev = space.int_w(w_instr_prev)
 
         self._setcellvars(cellvars)
+        # XXX what if the frame is in another thread??
         space.frame_trace_action.fire()
 
     def hide(self):

File pypy/interpreter/pyopcode.py

 
     def handle_operation_error(self, ec, operr, attach_tb=True):
         if attach_tb:
-            if not jit.we_are_jitted():
+            if 1:
                 # xxx this is a hack.  It allows bytecode_trace() to
                 # call a signal handler which raises, and catch the
                 # raised exception immediately.  See test_alarm_raise in
                     trace = self.w_f_trace
                     self.w_f_trace = None
                     try:
-                        ec.bytecode_trace(self)
+                        ec.bytecode_trace_after_exception(self)
                     finally:
                         self.w_f_trace = trace
                 except OperationError, e:
                     operr = e
             pytraceback.record_application_traceback(
                 self.space, operr, self, self.last_instr)
-            if not jit.we_are_jitted():
-                ec.exception_trace(self, operr)
+            ec.exception_trace(self, operr)
 
         block = self.unrollstack(SApplicationException.kind)
         if block is None:
         arguments = f.popvalues(n_arguments)
         args = f.argument_factory(arguments, keywords, keywords_w, w_star, w_starstar)
         w_function  = f.popvalue()
-        if jit.we_are_jitted():
+        if f.is_being_profiled and is_builtin_code(w_function):
+            w_result = f.space.call_args_and_c_profile(f, w_function, args)
+        else:
             w_result = f.space.call_args(w_function, args)
-        else:
-            if f.is_being_profiled and is_builtin_code(w_function):
-                w_result = f.space.call_args_and_c_profile(f, w_function, args)
-            else:
-                w_result = f.space.call_args(w_function, args)
         rstack.resume_point("call_function", f, returns=w_result)
         f.pushvalue(w_result)
         

File pypy/jit/metainterp/test/test_basic.py

         res = self.meta_interp(f, [55])
         assert res == -1
 
+    def test_confirm_enter_jit(self):
+        def confirm_enter_jit(x, y):
+            return x <= 5
+        myjitdriver = JitDriver(greens = ['x'], reds = ['y'],
+                                confirm_enter_jit = confirm_enter_jit)
+        def f(x, y):
+            while y >= 0:
+                myjitdriver.can_enter_jit(x=x, y=y)
+                myjitdriver.jit_merge_point(x=x, y=y)
+                y -= x
+            return y
+        #
+        res = self.meta_interp(f, [10, 84])
+        assert res == -6
+        self.check_loop_count(0)
+        #
+        res = self.meta_interp(f, [3, 19])
+        assert res == -2
+        self.check_loop_count(1)
+
     def test_format(self):
         def f(n):
             return len("<%d>" % n)

File pypy/jit/metainterp/test/test_warmstate.py

     class FakeWarmRunnerDesc:
         can_inline_ptr = None
         get_printable_location_ptr = None
+        confirm_enter_jit_ptr = None
         green_args_spec = [lltype.Signed, lltype.Float]
     class FakeCell:
         dont_trace_here = False
         green_args_spec = [lltype.Signed, lltype.Float]
         can_inline_ptr = llhelper(CAN_INLINE, can_inline)
         get_printable_location_ptr = None
+        confirm_enter_jit_ptr = None
     state = WarmEnterState(FakeWarmRunnerDesc())
     def jit_getter(*args):
         return FakeCell()
         green_args_spec = [lltype.Signed, lltype.Float]
         can_inline_ptr = None
         get_printable_location_ptr = llhelper(GET_LOCATION, get_location)
+        confirm_enter_jit_ptr = None
     state = WarmEnterState(FakeWarmRunnerDesc())
     state.make_jitdriver_callbacks()
     res = state.get_location_str([BoxInt(5), BoxFloat(42.5)])
     assert res == "hi there"
+
+def test_make_jitdriver_callbacks_4():
+    def confirm_enter_jit(x, y, z):
+        assert x == 5
+        assert y == 42.5
+        assert z == 3
+        return True
+    ENTER_JIT = lltype.Ptr(lltype.FuncType([lltype.Signed, lltype.Float,
+                                            lltype.Signed], lltype.Bool))
+    class FakeWarmRunnerDesc:
+        rtyper = None
+        green_args_spec = [lltype.Signed, lltype.Float]
+        can_inline_ptr = None
+        get_printable_location_ptr = None
+        confirm_enter_jit_ptr = llhelper(ENTER_JIT, confirm_enter_jit)
+    state = WarmEnterState(FakeWarmRunnerDesc())
+    state.make_jitdriver_callbacks()
+    res = state.confirm_enter_jit(5, 42.5, 3)
+    assert res is True

File pypy/jit/metainterp/warmspot.py

             annhelper, self.jitdriver.can_inline, annmodel.s_Bool)
         self.get_printable_location_ptr = self._make_hook_graph(
             annhelper, self.jitdriver.get_printable_location, s_Str)
+        self.confirm_enter_jit_ptr = self._make_hook_graph(
+            annhelper, self.jitdriver.confirm_enter_jit, annmodel.s_Bool,
+            onlygreens=False)
         annhelper.finish()
 
-    def _make_hook_graph(self, annhelper, func, s_result, s_first_arg=None):
+    def _make_hook_graph(self, annhelper, func, s_result, s_first_arg=None,
+                         onlygreens=True):
         if func is None:
             return None
         #
         if s_first_arg is not None:
             extra_args_s.append(s_first_arg)
         #
-        args_s = self.portal_args_s[:len(self.green_args_spec)]
+        args_s = self.portal_args_s
+        if onlygreens:
+            args_s = args_s[:len(self.green_args_spec)]
         graph = annhelper.getgraph(func, extra_args_s + args_s, s_result)
         funcptr = annhelper.graph2delayed(graph)
         return funcptr

File pypy/jit/metainterp/warmstate.py

         get_jitcell = self.make_jitcell_getter()
         set_future_values = self.make_set_future_values()
         self.make_jitdriver_callbacks()
+        confirm_enter_jit = self.confirm_enter_jit
 
         def maybe_compile_and_run(*args):
             """Entry point to the JIT.  Called at the point with the
                     cell.counter = n
                     return
                 # bound reached; start tracing
+                if not confirm_enter_jit(*args):
+                    cell.counter = 0
+                    return
                 from pypy.jit.metainterp.pyjitpl import MetaInterp
                 metainterp = MetaInterp(metainterp_sd)
                 try:
                     self.disable_noninlinable_function(metainterp)
                     raise
             else:
+                if not confirm_enter_jit(*args):
+                    return
                 # machine code was already compiled for these greenargs
                 # get the assembler and fill in the boxes
                 set_future_values(*args[num_green_args:])
             greenargs = unwrap_greenkey(greenkey)
             return can_inline(*greenargs)
         self.can_inline_callable = can_inline_greenkey
-
         #
         get_location_ptr = self.warmrunnerdesc.get_printable_location_ptr
         if get_location_ptr is None:
                     res = hlstr(res)
                 return res
         self.get_location_str = get_location_str
+        #
+        confirm_enter_jit_ptr = self.warmrunnerdesc.confirm_enter_jit_ptr
+        if confirm_enter_jit_ptr is None:
+            def confirm_enter_jit(*args):
+                return True
+        else:
+            rtyper = self.warmrunnerdesc.rtyper
+            #
+            def confirm_enter_jit(*args):
+                fn = support.maybe_on_top_of_llinterp(rtyper,
+                                                      confirm_enter_jit_ptr)
+                return fn(*args)
+        self.confirm_enter_jit = confirm_enter_jit

File pypy/module/pypyjit/interp_jit.py

 def set_jitcell_at(newcell, next_instr, bytecode):
     bytecode.jit_cells[next_instr] = newcell
 
+def confirm_enter_jit(next_instr, bytecode, frame, ec):
+    return (frame.w_f_trace is None and
+            ec.profilefunc is None and
+            ec.w_tracefunc is None)
+
 
 class PyPyJitDriver(JitDriver):
     reds = ['frame', 'ec']
                               get_printable_location = get_printable_location,
                               leave = leave,
                               get_jitcell_at = get_jitcell_at,
-                              set_jitcell_at = set_jitcell_at)
+                              set_jitcell_at = set_jitcell_at,
+                              confirm_enter_jit = confirm_enter_jit)
 
 class __extend__(PyFrame):
 

File pypy/rlib/jit.py

         hop.exception_cannot_occur()
         return hop.inputconst(lltype.Signed, _we_are_jitted)
 
+
+##def force_virtualizable(virtualizable):
+##    pass
+
+##class Entry(ExtRegistryEntry):
+##    _about_ = force_virtualizable
+
+##    def compute_result_annotation(self):
+##        from pypy.annotation import model as annmodel
+##        return annmodel.s_None
+
+##    def specialize_call(self, hop):
+##        [vinst] = hop.inputargs(hop.args_r[0])
+##        cname = inputconst(lltype.Void, None)
+##        cflags = inputconst(lltype.Void, {})
+##        hop.exception_cannot_occur()
+##        return hop.genop('jit_force_virtualizable', [vinst, cname, cflags],
+##                         resulttype=lltype.Void)
+
 # ____________________________________________________________
 # User interface for the hotpath JIT policy
 
     def __init__(self, greens=None, reds=None, virtualizables=None,
                  get_jitcell_at=None, set_jitcell_at=None,
                  can_inline=None, get_printable_location=None,
-                 leave=None):
+                 confirm_enter_jit=None,
+                 leave=None):   # XXX 'leave' is deprecated
         if greens is not None:
             self.greens = greens
         if reds is not None:
         self.set_jitcell_at = set_jitcell_at
         self.get_printable_location = get_printable_location
         self.can_inline = can_inline
+        self.confirm_enter_jit = confirm_enter_jit
         self.leave = leave
 
     def _freeze_(self):