Author: Armin Rigo <[email protected]>
Branch: 
Changeset: r85631:9927e7ba72e9
Date: 2016-07-09 18:14 +0200
http://bitbucket.org/pypy/pypy/changeset/9927e7ba72e9/

Log:    hg merge issue2335

        Hopefully, this is a real fix for the issue shown in #2200 and
        #2235. It should avoid a remaining case in the JIT, where successive
        guard failures in the same Python function end up as successive
        levels of RPython functions, eventually exhausting the stack, while
        at app-level the traceback is very short.

diff --git a/rpython/jit/metainterp/blackhole.py 
b/rpython/jit/metainterp/blackhole.py
--- a/rpython/jit/metainterp/blackhole.py
+++ b/rpython/jit/metainterp/blackhole.py
@@ -1143,45 +1143,35 @@
 
     @arguments("cpu", "i", "R", "d", returns="i")
     def bhimpl_residual_call_r_i(cpu, func, args_r, calldescr):
-        workaround2200.active = True
         return cpu.bh_call_i(func, None, args_r, None, calldescr)
     @arguments("cpu", "i", "R", "d", returns="r")
     def bhimpl_residual_call_r_r(cpu, func, args_r, calldescr):
-        workaround2200.active = True
         return cpu.bh_call_r(func, None, args_r, None, calldescr)
     @arguments("cpu", "i", "R", "d")
     def bhimpl_residual_call_r_v(cpu, func, args_r, calldescr):
-        workaround2200.active = True
         return cpu.bh_call_v(func, None, args_r, None, calldescr)
 
     @arguments("cpu", "i", "I", "R", "d", returns="i")
     def bhimpl_residual_call_ir_i(cpu, func, args_i, args_r, calldescr):
-        workaround2200.active = True
         return cpu.bh_call_i(func, args_i, args_r, None, calldescr)
     @arguments("cpu", "i", "I", "R", "d", returns="r")
     def bhimpl_residual_call_ir_r(cpu, func, args_i, args_r, calldescr):
-        workaround2200.active = True
         return cpu.bh_call_r(func, args_i, args_r, None, calldescr)
     @arguments("cpu", "i", "I", "R", "d")
     def bhimpl_residual_call_ir_v(cpu, func, args_i, args_r, calldescr):
-        workaround2200.active = True
         return cpu.bh_call_v(func, args_i, args_r, None, calldescr)
 
     @arguments("cpu", "i", "I", "R", "F", "d", returns="i")
     def bhimpl_residual_call_irf_i(cpu, func, args_i,args_r,args_f,calldescr):
-        workaround2200.active = True
         return cpu.bh_call_i(func, args_i, args_r, args_f, calldescr)
     @arguments("cpu", "i", "I", "R", "F", "d", returns="r")
     def bhimpl_residual_call_irf_r(cpu, func, args_i,args_r,args_f,calldescr):
-        workaround2200.active = True
         return cpu.bh_call_r(func, args_i, args_r, args_f, calldescr)
     @arguments("cpu", "i", "I", "R", "F", "d", returns="f")
     def bhimpl_residual_call_irf_f(cpu, func, args_i,args_r,args_f,calldescr):
-        workaround2200.active = True
         return cpu.bh_call_f(func, args_i, args_r, args_f, calldescr)
     @arguments("cpu", "i", "I", "R", "F", "d")
     def bhimpl_residual_call_irf_v(cpu, func, args_i,args_r,args_f,calldescr):
-        workaround2200.active = True
         return cpu.bh_call_v(func, args_i, args_r, args_f, calldescr)
 
     # conditional calls - note that they cannot return stuff
@@ -1209,54 +1199,44 @@
 
     @arguments("cpu", "j", "R", returns="i")
     def bhimpl_inline_call_r_i(cpu, jitcode, args_r):
-        workaround2200.active = True
         return cpu.bh_call_i(jitcode.get_fnaddr_as_int(),
                              None, args_r, None, jitcode.calldescr)
     @arguments("cpu", "j", "R", returns="r")
     def bhimpl_inline_call_r_r(cpu, jitcode, args_r):
-        workaround2200.active = True
         return cpu.bh_call_r(jitcode.get_fnaddr_as_int(),
                              None, args_r, None, jitcode.calldescr)
     @arguments("cpu", "j", "R")
     def bhimpl_inline_call_r_v(cpu, jitcode, args_r):
-        workaround2200.active = True
         return cpu.bh_call_v(jitcode.get_fnaddr_as_int(),
                              None, args_r, None, jitcode.calldescr)
 
     @arguments("cpu", "j", "I", "R", returns="i")
     def bhimpl_inline_call_ir_i(cpu, jitcode, args_i, args_r):
-        workaround2200.active = True
         return cpu.bh_call_i(jitcode.get_fnaddr_as_int(),
                              args_i, args_r, None, jitcode.calldescr)
     @arguments("cpu", "j", "I", "R", returns="r")
     def bhimpl_inline_call_ir_r(cpu, jitcode, args_i, args_r):
-        workaround2200.active = True
         return cpu.bh_call_r(jitcode.get_fnaddr_as_int(),
                              args_i, args_r, None, jitcode.calldescr)
     @arguments("cpu", "j", "I", "R")
     def bhimpl_inline_call_ir_v(cpu, jitcode, args_i, args_r):
-        workaround2200.active = True
         return cpu.bh_call_v(jitcode.get_fnaddr_as_int(),
                              args_i, args_r, None, jitcode.calldescr)
 
     @arguments("cpu", "j", "I", "R", "F", returns="i")
     def bhimpl_inline_call_irf_i(cpu, jitcode, args_i, args_r, args_f):
-        workaround2200.active = True
         return cpu.bh_call_i(jitcode.get_fnaddr_as_int(),
                              args_i, args_r, args_f, jitcode.calldescr)
     @arguments("cpu", "j", "I", "R", "F", returns="r")
     def bhimpl_inline_call_irf_r(cpu, jitcode, args_i, args_r, args_f):
-        workaround2200.active = True
         return cpu.bh_call_r(jitcode.get_fnaddr_as_int(),
                              args_i, args_r, args_f, jitcode.calldescr)
     @arguments("cpu", "j", "I", "R", "F", returns="f")
     def bhimpl_inline_call_irf_f(cpu, jitcode, args_i, args_r, args_f):
-        workaround2200.active = True
         return cpu.bh_call_f(jitcode.get_fnaddr_as_int(),
                              args_i, args_r, args_f, jitcode.calldescr)
     @arguments("cpu", "j", "I", "R", "F")
     def bhimpl_inline_call_irf_v(cpu, jitcode, args_i, args_r, args_f):
-        workaround2200.active = True
         return cpu.bh_call_v(jitcode.get_fnaddr_as_int(),
                              args_i, args_r, args_f, jitcode.calldescr)
 
@@ -1543,8 +1523,6 @@
             if not self.nextblackholeinterp:
                 self._exit_frame_with_exception(current_exc)
             return current_exc
-        finally:
-            workaround2200.active = False
         #
         # pass the frame's return value to the caller
         caller = self.nextblackholeinterp
@@ -1717,10 +1695,3 @@
     #
     _run_forever(firstbh, current_exc)
 convert_and_run_from_pyjitpl._dont_inline_ = True
-
-# ____________________________________________________________
-
-class WorkaroundIssue2200(object):
-    pass
-workaround2200 = WorkaroundIssue2200()
-workaround2200.active = False
diff --git a/rpython/jit/metainterp/compile.py 
b/rpython/jit/metainterp/compile.py
--- a/rpython/jit/metainterp/compile.py
+++ b/rpython/jit/metainterp/compile.py
@@ -620,23 +620,28 @@
         raise jitexc.DoneWithThisFrameVoid()
 
 class DoneWithThisFrameDescrInt(_DoneWithThisFrameDescr):
+    def get_result(self, cpu, deadframe):
+        return cpu.get_int_value(deadframe, 0)
     def handle_fail(self, deadframe, metainterp_sd, jitdriver_sd):
         assert jitdriver_sd.result_type == history.INT
-        result = metainterp_sd.cpu.get_int_value(deadframe, 0)
-        raise jitexc.DoneWithThisFrameInt(result)
+        cpu = metainterp_sd.cpu
+        raise jitexc.DoneWithThisFrameInt(self.get_result(cpu, deadframe))
 
 class DoneWithThisFrameDescrRef(_DoneWithThisFrameDescr):
+    def get_result(self, cpu, deadframe):
+        return cpu.get_ref_value(deadframe, 0)
     def handle_fail(self, deadframe, metainterp_sd, jitdriver_sd):
         assert jitdriver_sd.result_type == history.REF
         cpu = metainterp_sd.cpu
-        result = cpu.get_ref_value(deadframe, 0)
-        raise jitexc.DoneWithThisFrameRef(cpu, result)
+        raise jitexc.DoneWithThisFrameRef(cpu, self.get_result(cpu, deadframe))
 
 class DoneWithThisFrameDescrFloat(_DoneWithThisFrameDescr):
+    def get_result(self, cpu, deadframe):
+        return cpu.get_float_value(deadframe, 0)
     def handle_fail(self, deadframe, metainterp_sd, jitdriver_sd):
         assert jitdriver_sd.result_type == history.FLOAT
-        result = metainterp_sd.cpu.get_float_value(deadframe, 0)
-        raise jitexc.DoneWithThisFrameFloat(result)
+        cpu = metainterp_sd.cpu
+        raise jitexc.DoneWithThisFrameFloat(self.get_result(cpu, deadframe))
 
 class ExitFrameWithExceptionDescrRef(_DoneWithThisFrameDescr):
     def handle_fail(self, deadframe, metainterp_sd, jitdriver_sd):
diff --git a/rpython/jit/metainterp/pyjitpl.py 
b/rpython/jit/metainterp/pyjitpl.py
--- a/rpython/jit/metainterp/pyjitpl.py
+++ b/rpython/jit/metainterp/pyjitpl.py
@@ -2543,7 +2543,13 @@
             elif box.type == history.REF: args.append(box.getref_base())
             elif box.type == history.FLOAT: args.append(box.getfloatstorage())
             else: assert 0
-        self.jitdriver_sd.warmstate.execute_assembler(loop_token, *args)
+        res = self.jitdriver_sd.warmstate.execute_assembler(loop_token, *args)
+        kind = history.getkind(lltype.typeOf(res))
+        if kind == 'void':  raise jitexc.DoneWithThisFrameVoid()
+        if kind == 'int':   raise jitexc.DoneWithThisFrameInt(res)
+        if kind == 'ref':   raise jitexc.DoneWithThisFrameRef(self.cpu, res)
+        if kind == 'float': raise jitexc.DoneWithThisFrameFloat(res)
+        raise AssertionError(kind)
 
     def prepare_resume_from_failure(self, deadframe, inputargs, resumedescr):
         exception = self.cpu.grab_exc_value(deadframe)
diff --git a/rpython/jit/metainterp/warmspot.py 
b/rpython/jit/metainterp/warmspot.py
--- a/rpython/jit/metainterp/warmspot.py
+++ b/rpython/jit/metainterp/warmspot.py
@@ -529,7 +529,7 @@
     def make_enter_function(self, jd):
         from rpython.jit.metainterp.warmstate import WarmEnterState
         state = WarmEnterState(self, jd)
-        maybe_compile_and_run = state.make_entry_point()
+        maybe_compile_and_run, EnterJitAssembler = state.make_entry_point()
         jd.warmstate = state
 
         def crash_in_jit(e):
@@ -560,6 +560,7 @@
         maybe_enter_jit._always_inline_ = True
         jd._maybe_enter_jit_fn = maybe_enter_jit
         jd._maybe_compile_and_run_fn = maybe_compile_and_run
+        jd._EnterJitAssembler = EnterJitAssembler
 
     def make_driverhook_graphs(self):
         s_Str = annmodel.SomeString()
@@ -842,12 +843,8 @@
         #           while 1:
         #               try:
         #                   return portal(*args)
-        #               except ContinueRunningNormally, e:
-        #                   *args = *e.new_args
-        #               except DoneWithThisFrame, e:
-        #                   return e.return
-        #               except ExitFrameWithException, e:
-        #                   raise Exception, e.value
+        #               except JitException, e:
+        #                   return handle_jitexception(e)
         #
         #       def portal(*args):
         #           while 1:
@@ -884,90 +881,79 @@
         rtyper = self.translator.rtyper
         RESULT = PORTALFUNC.RESULT
         result_kind = history.getkind(RESULT)
+        assert result_kind.startswith(jd.result_type)
         ts = self.cpu.ts
         state = jd.warmstate
         maybe_compile_and_run = jd._maybe_compile_and_run_fn
+        EnterJitAssembler = jd._EnterJitAssembler
 
         def ll_portal_runner(*args):
-            start = True
-            while 1:
-                try:
-                    # maybe enter from the function's start.  Note that the
-                    # 'start' variable is constant-folded away because it's
-                    # the first statement in the loop.
-                    if start:
-                        maybe_compile_and_run(
-                            state.increment_function_threshold, *args)
-                    #
-                    # then run the normal portal function, i.e. the
-                    # interpreter's main loop.  It might enter the jit
-                    # via maybe_enter_jit(), which typically ends with
-                    # handle_fail() being called, which raises on the
-                    # following exceptions --- catched here, because we
-                    # want to interrupt the whole interpreter loop.
-                    return support.maybe_on_top_of_llinterp(rtyper,
-                                                      portal_ptr)(*args)
-                except jitexc.ContinueRunningNormally as e:
+            try:
+                # maybe enter from the function's start.
+                maybe_compile_and_run(
+                    state.increment_function_threshold, *args)
+                #
+                # then run the normal portal function, i.e. the
+                # interpreter's main loop.  It might enter the jit
+                # via maybe_enter_jit(), which typically ends with
+                # handle_fail() being called, which raises on the
+                # following exceptions --- catched here, because we
+                # want to interrupt the whole interpreter loop.
+                return support.maybe_on_top_of_llinterp(rtyper,
+                                                  portal_ptr)(*args)
+            except jitexc.JitException as e:
+                result = handle_jitexception(e)
+                if result_kind != 'void':
+                    result = specialize_value(RESULT, result)
+                return result
+
+        def handle_jitexception(e):
+            # XXX there are too many exceptions all around...
+            while True:
+                if isinstance(e, EnterJitAssembler):
+                    try:
+                        return e.execute()
+                    except jitexc.JitException as e:
+                        continue
+                #
+                if isinstance(e, jitexc.ContinueRunningNormally):
                     args = ()
                     for ARGTYPE, attrname, count in portalfunc_ARGS:
                         x = getattr(e, attrname)[count]
                         x = specialize_value(ARGTYPE, x)
                         args = args + (x,)
-                    start = False
-                    continue
-                except jitexc.DoneWithThisFrameVoid:
-                    assert result_kind == 'void'
-                    return
-                except jitexc.DoneWithThisFrameInt as e:
-                    assert result_kind == 'int'
-                    return specialize_value(RESULT, e.result)
-                except jitexc.DoneWithThisFrameRef as e:
-                    assert result_kind == 'ref'
-                    return specialize_value(RESULT, e.result)
-                except jitexc.DoneWithThisFrameFloat as e:
-                    assert result_kind == 'float'
-                    return specialize_value(RESULT, e.result)
-                except jitexc.ExitFrameWithExceptionRef as e:
+                    try:
+                        result = support.maybe_on_top_of_llinterp(rtyper,
+                                                            portal_ptr)(*args)
+                    except jitexc.JitException as e:
+                        continue
+                    if result_kind != 'void':
+                        result = unspecialize_value(result)
+                    return result
+                #
+                if result_kind == 'void':
+                    if isinstance(e, jitexc.DoneWithThisFrameVoid):
+                        return None
+                if result_kind == 'int':
+                    if isinstance(e, jitexc.DoneWithThisFrameInt):
+                        return e.result
+                if result_kind == 'ref':
+                    if isinstance(e, jitexc.DoneWithThisFrameRef):
+                        return e.result
+                if result_kind == 'float':
+                    if isinstance(e, jitexc.DoneWithThisFrameFloat):
+                        return e.result
+                #
+                if isinstance(e, jitexc.ExitFrameWithExceptionRef):
                     value = ts.cast_to_baseclass(e.value)
                     if not we_are_translated():
                         raise LLException(ts.get_typeptr(value), value)
                     else:
                         value = cast_base_ptr_to_instance(Exception, value)
+                        assert value is not None
                         raise value
-
-        def handle_jitexception(e):
-            # XXX the bulk of this function is mostly a copy-paste from above
-            try:
-                raise e
-            except jitexc.ContinueRunningNormally as e:
-                args = ()
-                for ARGTYPE, attrname, count in portalfunc_ARGS:
-                    x = getattr(e, attrname)[count]
-                    x = specialize_value(ARGTYPE, x)
-                    args = args + (x,)
-                result = ll_portal_runner(*args)
-                if result_kind != 'void':
-                    result = unspecialize_value(result)
-                return result
-            except jitexc.DoneWithThisFrameVoid:
-                assert result_kind == 'void'
-                return
-            except jitexc.DoneWithThisFrameInt as e:
-                assert result_kind == 'int'
-                return e.result
-            except jitexc.DoneWithThisFrameRef as e:
-                assert result_kind == 'ref'
-                return e.result
-            except jitexc.DoneWithThisFrameFloat as e:
-                assert result_kind == 'float'
-                return e.result
-            except jitexc.ExitFrameWithExceptionRef as e:
-                value = ts.cast_to_baseclass(e.value)
-                if not we_are_translated():
-                    raise LLException(ts.get_typeptr(value), value)
-                else:
-                    value = cast_base_ptr_to_instance(Exception, value)
-                    raise value
+                #
+                raise AssertionError("all cases should have been handled")
 
         jd._ll_portal_runner = ll_portal_runner # for debugging
         jd.portal_runner_ptr = self.helper_func(jd._PTR_PORTAL_FUNCTYPE,
diff --git a/rpython/jit/metainterp/warmstate.py 
b/rpython/jit/metainterp/warmstate.py
--- a/rpython/jit/metainterp/warmstate.py
+++ b/rpython/jit/metainterp/warmstate.py
@@ -2,7 +2,7 @@
 import weakref
 
 from rpython.jit.codewriter import support, heaptracker, longlong
-from rpython.jit.metainterp import resoperation, history
+from rpython.jit.metainterp import resoperation, history, jitexc
 from rpython.rlib.debug import debug_start, debug_stop, debug_print
 from rpython.rlib.debug import have_debug_prints_for
 from rpython.rlib.jit import PARAMETERS
@@ -348,8 +348,9 @@
 
     def make_entry_point(self):
         "NOT_RPYTHON"
-        if hasattr(self, 'maybe_compile_and_run'):
-            return self.maybe_compile_and_run
+        from rpython.jit.metainterp import compile
+        if hasattr(self, 'entry_point_fns'):
+            return self.entry_point_fns
 
         warmrunnerdesc = self.warmrunnerdesc
         metainterp_sd = warmrunnerdesc.metainterp_sd
@@ -362,6 +363,8 @@
         confirm_enter_jit = self.confirm_enter_jit
         range_red_args = unrolling_iterable(
             range(num_green_args, num_green_args + jitdriver_sd.num_red_args))
+        name_red_args = unrolling_iterable(
+            [(i, 'arg%d' % i) for i in range(jitdriver_sd.num_red_args)])
         # get a new specialized copy of the method
         ARGS = []
         for kind in jitdriver_sd.red_args_types:
@@ -376,6 +379,7 @@
         func_execute_token = self.cpu.make_execute_token(*ARGS)
         cpu = self.cpu
         jitcounter = self.warmrunnerdesc.jitcounter
+        result_type = jitdriver_sd.result_type
 
         def execute_assembler(loop_token, *args):
             # Call the backend to run the 'looptoken' with the given
@@ -396,8 +400,23 @@
             #
             # Handle the failure
             fail_descr = cpu.get_latest_descr(deadframe)
+            # First, a fast path to avoid raising and immediately catching
+            # a DoneWithThisFrame exception
+            if result_type == history.VOID:
+                if isinstance(fail_descr, compile.DoneWithThisFrameDescrVoid):
+                    return None
+            if result_type == history.INT:
+                if isinstance(fail_descr, compile.DoneWithThisFrameDescrInt):
+                    return fail_descr.get_result(cpu, deadframe)
+            if result_type == history.REF:
+                if isinstance(fail_descr, compile.DoneWithThisFrameDescrRef):
+                    return fail_descr.get_result(cpu, deadframe)
+            if result_type == history.FLOAT:
+                if isinstance(fail_descr, compile.DoneWithThisFrameDescrFloat):
+                    return fail_descr.get_result(cpu, deadframe)
+            #
+            # General case
             fail_descr.handle_fail(deadframe, metainterp_sd, jitdriver_sd)
-            #
             assert 0, "should have raised"
 
         def bound_reached(hash, cell, *args):
@@ -419,7 +438,8 @@
 
         def maybe_compile_and_run(increment_threshold, *args):
             """Entry point to the JIT.  Called at the point with the
-            can_enter_jit() hint.
+            can_enter_jit() hint, and at the start of a function
+            with a different threshold.
             """
             # Look for the cell corresponding to the current greenargs.
             # Search for the JitCell that is of the correct subclass of
@@ -439,14 +459,6 @@
                     bound_reached(hash, None, *args)
                 return
 
-            # Workaround for issue #2200, maybe temporary.  This is not
-            # a proper fix, but only a hack that should work well enough
-            # for PyPy's main jitdriver...  See test_issue2200_recursion
-            from rpython.jit.metainterp.blackhole import workaround2200
-            if workaround2200.active:
-                workaround2200.active = False
-                return
-
             # Here, we have found 'cell'.
             #
             if cell.flags & (JC_TRACING | JC_TEMPORARY):
@@ -484,14 +496,27 @@
             execute_args = ()
             for i in range_red_args:
                 execute_args += (unspecialize_value(args[i]), )
-            # run it!  this executes until interrupted by an exception
-            execute_assembler(procedure_token, *execute_args)
-            assert 0, "should not reach this point"
+            # run it, but from outside in ll_portal_runner, not from here
+            # (this avoids RPython-level recursion with no corresponding
+            # app-level recursion, as shown by issues 2200 and 2335)
+            raise EnterJitAssembler(procedure_token, *execute_args)
+
+        class EnterJitAssembler(jitexc.JitException):
+            def __init__(self, procedure_token, *args):
+                self.procedure_token = procedure_token
+                for i, argname in name_red_args:
+                    setattr(self, argname, args[i])
+            def execute(self):
+                args = ()
+                for i, argname in name_red_args:
+                    args += (getattr(self, argname), )
+                return execute_assembler(self.procedure_token, *args)
 
         maybe_compile_and_run._dont_inline_ = True
-        self.maybe_compile_and_run = maybe_compile_and_run
         self.execute_assembler = execute_assembler
-        return maybe_compile_and_run
+        self.entry_point_fns = (maybe_compile_and_run,
+                                EnterJitAssembler)
+        return self.entry_point_fns
 
     # ----------
 
_______________________________________________
pypy-commit mailing list
[email protected]
https://mail.python.org/mailman/listinfo/pypy-commit

Reply via email to