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