Blob Blame History Raw
diff --git a/rpython/jit/metainterp/blackhole.py b/rpython/jit/metainterp/blackhole.py
index 9b14943..b9d1c4c 100644
--- a/rpython/jit/metainterp/blackhole.py
+++ b/rpython/jit/metainterp/blackhole.py
@@ -1052,35 +1052,45 @@ class BlackholeInterpreter(object):
 
     @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
@@ -1108,44 +1118,54 @@ class BlackholeInterpreter(object):
 
     @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)
 
@@ -1438,6 +1458,8 @@ class BlackholeInterpreter(object):
             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
@@ -1656,3 +1678,10 @@ def convert_and_run_from_pyjitpl(metainterp, raising_exception=False):
         current_exc = lltype.nullptr(rclass.OBJECTPTR.TO)
     #
     _run_forever(firstbh, current_exc)
+
+# ____________________________________________________________
+
+class WorkaroundIssue2200(object):
+    pass
+workaround2200 = WorkaroundIssue2200()
+workaround2200.active = False
diff --git a/rpython/jit/metainterp/test/test_ajit.py b/rpython/jit/metainterp/test/test_ajit.py
index ad81456..c820185 100644
--- a/rpython/jit/metainterp/test/test_ajit.py
+++ b/rpython/jit/metainterp/test/test_ajit.py
@@ -4044,3 +4044,30 @@ class TestLLtype(BaseLLtypeTests, LLJitMixin):
         res = self.interp_operations(f, [17])
         assert res == 42
         self.check_operations_history(guard_true=1, guard_false=0)
+
+    def test_issue2200_recursion(self):
+        # Reproduces issue #2200.  This test contains no recursion,
+        # but due to an unlikely combination of factors it ends up
+        # creating an RPython-level recursion, one per loop iteration.
+        # The recursion is: blackhole interp from the failing guard ->
+        # does the call to enter() as a normal call -> enter() runs
+        # can_enter_jit() as if we're interpreted -> we enter the JIT
+        # again from the start of the loop -> the guard fails again
+        # during the next iteration -> blackhole interp.  All arrows
+        # in the previous sentence are one or more levels of RPython
+        # function calls.
+        driver = JitDriver(greens=[], reds=["i"])
+        def enter(i):
+            driver.can_enter_jit(i=i)
+        def f():
+            set_param(None, 'trace_eagerness', 999999)
+            i = 0
+            while True:
+                driver.jit_merge_point(i=i)
+                i += 1
+                if i >= 300:
+                    return i
+                promote(i + 1)   # a failing guard
+                enter(i)
+
+        self.meta_interp(f, [])
diff --git a/rpython/jit/metainterp/warmstate.py b/rpython/jit/metainterp/warmstate.py
index 805933e..ca47f03 100644
--- a/rpython/jit/metainterp/warmstate.py
+++ b/rpython/jit/metainterp/warmstate.py
@@ -405,6 +405,14 @@ class WarmEnterState(object):
                     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):