Author: Antonio Cuni <[email protected]>
Branch:
Changeset: r44590:f3cf1cf52a70
Date: 2011-05-30 10:30 +0200
http://bitbucket.org/pypy/pypy/changeset/f3cf1cf52a70/
Log: (antocuni, arigo): merge the invalidate-virtualrefs branch; this
makes virtualrefs *less* powerful, because they can no longer be
forced after the call to virtual_ref_finish (unless they were
already forced before), but at the same time it lets the JIT to
optimize them better; in particular, we can avoid to allocate the
frames after a call whose effectinfo is none
diff --git a/pypy/interpreter/error.py b/pypy/interpreter/error.py
--- a/pypy/interpreter/error.py
+++ b/pypy/interpreter/error.py
@@ -11,14 +11,14 @@
"""Interpreter-level exception that signals an exception that should be
sent to the application level.
- OperationError instances have three public attributes (and no .args),
- w_type, w_value and application_traceback, which contain the wrapped
+ OperationError instances have three attributes (and no .args),
+ w_type, _w_value and _application_traceback, which contain the wrapped
type and value describing the exception, and a chained list of
PyTraceback objects making the application-level traceback.
"""
_w_value = None
- application_traceback = None
+ _application_traceback = None
def __init__(self, w_type, w_value, tb=None):
if not we_are_translated() and w_type is None:
@@ -26,7 +26,7 @@
raise FlowingError(w_value)
self.setup(w_type)
self._w_value = w_value
- self.application_traceback = tb
+ self._application_traceback = tb
def setup(self, w_type):
self.w_type = w_type
@@ -37,7 +37,7 @@
# for sys.exc_clear()
self.w_type = space.w_None
self._w_value = space.w_None
- self.application_traceback = None
+ self._application_traceback = None
if not we_are_translated():
del self.debug_excs[:]
@@ -103,7 +103,7 @@
def print_app_tb_only(self, file):
"NOT_RPYTHON"
- tb = self.application_traceback
+ tb = self._application_traceback
if tb:
import linecache
print >> file, "Traceback (application-level):"
@@ -251,6 +251,30 @@
def _compute_value(self):
raise NotImplementedError
+ def get_traceback(self):
+ """Calling this marks the PyTraceback as escaped, i.e. it becomes
+ accessible and inspectable by app-level Python code. For the JIT.
+ Note that this has no effect if there are already several traceback
+ frames recorded, because in this case they are already marked as
+ escaping by executioncontext.leave() being called with
+ got_exception=True.
+ """
+ from pypy.interpreter.pytraceback import PyTraceback
+ tb = self._application_traceback
+ if tb is not None and isinstance(tb, PyTraceback):
+ tb.frame.mark_as_escaped()
+ return tb
+
+ def set_traceback(self, traceback):
+ """Set the current traceback. It should either be a traceback
+ pointing to some already-escaped frame, or a traceback for the
+ current frame. To support the latter case we do not mark the
+ frame as escaped. The idea is that it will be marked as escaping
+ only if the exception really propagates out of this frame, by
+ executioncontext.leave() being called with got_exception=True.
+ """
+ self._application_traceback = traceback
+
# ____________________________________________________________
# optimization only: avoid the slowest operation -- the string
# formatting with '%' -- in the common case were we don't
diff --git a/pypy/interpreter/executioncontext.py
b/pypy/interpreter/executioncontext.py
--- a/pypy/interpreter/executioncontext.py
+++ b/pypy/interpreter/executioncontext.py
@@ -58,13 +58,23 @@
frame.f_backref = self.topframeref
self.topframeref = jit.virtual_ref(frame)
- def leave(self, frame, w_exitvalue):
+ def leave(self, frame, w_exitvalue, got_exception):
try:
if self.profilefunc:
self._trace(frame, 'leaveframe', w_exitvalue)
finally:
+ frame_vref = self.topframeref
self.topframeref = frame.f_backref
- jit.virtual_ref_finish(frame)
+ if frame.escaped or got_exception:
+ # if this frame escaped to applevel, we must ensure that also
+ # f_back does
+ f_back = frame.f_backref()
+ if f_back:
+ f_back.mark_as_escaped()
+ # force the frame (from the JIT point of view), so that it can
+ # be accessed also later
+ frame_vref()
+ jit.virtual_ref_finish(frame_vref, frame)
if self.w_tracefunc is not None and not frame.hide():
self.space.frame_trace_action.fire()
@@ -276,7 +286,7 @@
if operr is not None:
w_value = operr.get_w_value(space)
w_arg = space.newtuple([operr.w_type, w_value,
- space.wrap(operr.application_traceback)])
+ space.wrap(operr.get_traceback())])
frame.fast2locals()
self.is_tracing += 1
diff --git a/pypy/interpreter/main.py b/pypy/interpreter/main.py
--- a/pypy/interpreter/main.py
+++ b/pypy/interpreter/main.py
@@ -118,7 +118,7 @@
operationerr.normalize_exception(space)
w_type = operationerr.w_type
w_value = operationerr.get_w_value(space)
- w_traceback = space.wrap(operationerr.application_traceback)
+ w_traceback = space.wrap(operationerr.get_traceback())
# for debugging convenience we also insert the exception into
# the interpreter-level sys.last_xxx
diff --git a/pypy/interpreter/pyframe.py b/pypy/interpreter/pyframe.py
--- a/pypy/interpreter/pyframe.py
+++ b/pypy/interpreter/pyframe.py
@@ -49,6 +49,7 @@
instr_ub = 0
instr_prev_plus_one = 0
is_being_profiled = False
+ escaped = False # see mark_as_escaped()
def __init__(self, space, code, w_globals, closure):
self = hint(self, access_directly=True, fresh_virtualizable=True)
@@ -67,6 +68,15 @@
make_sure_not_resized(self.fastlocals_w)
self.f_lineno = code.co_firstlineno
+ def mark_as_escaped(self):
+ """
+ Must be called on frames that are exposed to applevel, e.g. by
+ sys._getframe(). This ensures that the virtualref holding the frame
+ is properly forced by ec.leave(), and thus the frame will be still
+ accessible even after the corresponding C stack died.
+ """
+ self.escaped = True
+
def append_block(self, block):
block.previous = self.lastblock
self.lastblock = block
@@ -138,6 +148,7 @@
not self.space.config.translating)
executioncontext = self.space.getexecutioncontext()
executioncontext.enter(self)
+ got_exception = True
w_exitvalue = self.space.w_None
try:
executioncontext.call_trace(self)
@@ -164,8 +175,9 @@
# clean up the exception, might be useful for not
# allocating exception objects in some cases
self.last_exception = None
+ got_exception = False
finally:
- executioncontext.leave(self, w_exitvalue)
+ executioncontext.leave(self, w_exitvalue, got_exception)
return w_exitvalue
execute_frame.insert_stack_check_here = True
@@ -312,7 +324,7 @@
w_tb = space.w_None
else:
w_exc_value = self.last_exception.get_w_value(space)
- w_tb = w(self.last_exception.application_traceback)
+ w_tb = w(self.last_exception.get_traceback())
tup_state = [
w(self.f_backref()),
@@ -633,7 +645,7 @@
while f is not None and f.last_exception is None:
f = f.f_backref()
if f is not None:
- return space.wrap(f.last_exception.application_traceback)
+ return space.wrap(f.last_exception.get_traceback())
return space.w_None
def fget_f_restricted(self, space):
diff --git a/pypy/interpreter/pyopcode.py b/pypy/interpreter/pyopcode.py
--- a/pypy/interpreter/pyopcode.py
+++ b/pypy/interpreter/pyopcode.py
@@ -566,7 +566,7 @@
else:
msg = "raise: arg 3 must be a traceback or None"
tb = pytraceback.check_traceback(space, w_traceback, msg)
- operror.application_traceback = tb
+ operror.set_traceback(tb)
# special 3-arguments raise, no new traceback obj will be attached
raise RaiseWithExplicitTraceback(operror)
@@ -946,7 +946,7 @@
isinstance(unroller, SApplicationException))
if is_app_exc:
operr = unroller.operr
- w_traceback = self.space.wrap(operr.application_traceback)
+ w_traceback = self.space.wrap(operr.get_traceback())
w_suppress = self.call_contextmanager_exit_function(
w_exitfunc,
operr.w_type,
diff --git a/pypy/interpreter/pytraceback.py b/pypy/interpreter/pytraceback.py
--- a/pypy/interpreter/pytraceback.py
+++ b/pypy/interpreter/pytraceback.py
@@ -51,9 +51,9 @@
def record_application_traceback(space, operror, frame, last_instruction):
if frame.pycode.hidden_applevel:
return
- tb = operror.application_traceback
+ tb = operror.get_traceback()
tb = PyTraceback(space, frame, last_instruction, tb)
- operror.application_traceback = tb
+ operror.set_traceback(tb)
def offset2lineno(c, stopat):
tab = c.co_lnotab
diff --git a/pypy/interpreter/test/test_pyframe.py
b/pypy/interpreter/test/test_pyframe.py
--- a/pypy/interpreter/test/test_pyframe.py
+++ b/pypy/interpreter/test/test_pyframe.py
@@ -98,6 +98,15 @@
return sys._getframe().f_back.f_code.co_name
f()
+ def test_f_back_virtualref(self):
+ import sys
+ def f():
+ return g()
+ def g():
+ return sys._getframe()
+ frame = f()
+ assert frame.f_back.f_code.co_name == 'f'
+
def test_f_exc_xxx(self):
import sys
@@ -122,6 +131,21 @@
except:
g(sys.exc_info())
+ def test_virtualref_through_traceback(self):
+ import sys
+ def g():
+ try:
+ raise ValueError
+ except:
+ _, _, tb = sys.exc_info()
+ return tb
+ def f():
+ return g()
+ #
+ tb = f()
+ assert tb.tb_frame.f_code.co_name == 'g'
+ assert tb.tb_frame.f_back.f_code.co_name == 'f'
+
def test_trace_basic(self):
import sys
l = []
diff --git a/pypy/jit/backend/x86/test/test_zrpy_gc.py
b/pypy/jit/backend/x86/test/test_zrpy_gc.py
--- a/pypy/jit/backend/x86/test/test_zrpy_gc.py
+++ b/pypy/jit/backend/x86/test/test_zrpy_gc.py
@@ -525,8 +525,8 @@
glob = A()
def f(n, x, x0, x1, x2, x3, x4, x5, x6, x7, l, s):
a = A()
- glob.v = virtual_ref(a)
- virtual_ref_finish(a)
+ glob.v = vref = virtual_ref(a)
+ virtual_ref_finish(vref, a)
n -= 1
return n, x, x0, x1, x2, x3, x4, x5, x6, x7, l, s
return None, f, None
diff --git a/pypy/jit/metainterp/optimizeopt/virtualize.py
b/pypy/jit/metainterp/optimizeopt/virtualize.py
--- a/pypy/jit/metainterp/optimizeopt/virtualize.py
+++ b/pypy/jit/metainterp/optimizeopt/virtualize.py
@@ -330,18 +330,28 @@
vrefvalue.setfield(descr_virtual_token, self.getvalue(tokenbox))
def optimize_VIRTUAL_REF_FINISH(self, op):
- # Set the 'forced' field of the virtual_ref.
- # In good cases, this is all virtual, so has no effect.
- # Otherwise, this forces the real object -- but only now, as
- # opposed to much earlier. This is important because the object is
- # typically a PyPy PyFrame, and now is the end of its execution, so
- # forcing it now does not have catastrophic effects.
+ # This operation is used in two cases. In normal cases, it
+ # is the end of the frame, and op.getarg(1) is NULL. In this
+ # case we just clear the vref.virtual_token, because it contains
+ # a stack frame address and we are about to leave the frame.
+ # In that case vref.forced should still be NULL, and remains
+ # NULL; and accessing the frame through the vref later is
+ # *forbidden* and will raise InvalidVirtualRef.
+ #
+ # In the other (uncommon) case, the operation is produced
+ # earlier, because the vref was forced during tracing already.
+ # In this case, op.getarg(1) is the virtual to force, and we
+ # have to store it in vref.forced.
+ #
vrefinfo = self.optimizer.metainterp_sd.virtualref_info
- # op.getarg(1) should really never point to null here
+ seo = self.optimizer.send_extra_operation
+
# - set 'forced' to point to the real object
- seo = self.optimizer.send_extra_operation
- seo(ResOperation(rop.SETFIELD_GC, op.getarglist(), None,
- descr = vrefinfo.descr_forced))
+ objbox = op.getarg(1)
+ if not self.optimizer.cpu.ts.CONST_NULL.same_constant(objbox):
+ seo(ResOperation(rop.SETFIELD_GC, op.getarglist(), None,
+ descr = vrefinfo.descr_forced))
+
# - set 'virtual_token' to TOKEN_NONE
args = [op.getarg(0), ConstInt(vrefinfo.TOKEN_NONE)]
seo(ResOperation(rop.SETFIELD_GC, args, None,
diff --git a/pypy/jit/metainterp/pyjitpl.py b/pypy/jit/metainterp/pyjitpl.py
--- a/pypy/jit/metainterp/pyjitpl.py
+++ b/pypy/jit/metainterp/pyjitpl.py
@@ -4,7 +4,7 @@
from pypy.rlib.unroll import unrolling_iterable
from pypy.rlib.debug import debug_start, debug_stop, debug_print
from pypy.rlib.debug import make_sure_not_resized
-from pypy.rlib import nonconst
+from pypy.rlib import nonconst, rstack
from pypy.jit.metainterp import history, compile, resume
from pypy.jit.metainterp.history import Const, ConstInt, ConstPtr, ConstFloat
@@ -1049,8 +1049,10 @@
vrefinfo = metainterp.staticdata.virtualref_info
vref = vrefbox.getref_base()
if vrefinfo.is_virtual_ref(vref):
+ # XXX write a comment about nullbox
+ nullbox = self.metainterp.cpu.ts.CONST_NULL
metainterp.history.record(rop.VIRTUAL_REF_FINISH,
- [vrefbox, lastbox], None)
+ [vrefbox, nullbox], None)
@arguments()
def opimpl_ll_read_timestamp(self):
@@ -2052,10 +2054,16 @@
def initialize_state_from_guard_failure(self, resumedescr):
# guard failure: rebuild a complete MIFrame stack
- self.in_recursion = -1 # always one portal around
- self.history = history.History()
- inputargs_and_holes = self.rebuild_state_after_failure(resumedescr)
- self.history.inputargs = [box for box in inputargs_and_holes if box]
+ # This is stack-critical code: it must not be interrupted by
StackOverflow,
+ # otherwise the jit_virtual_refs are left in a dangling state.
+ rstack._stack_criticalcode_start()
+ try:
+ self.in_recursion = -1 # always one portal around
+ self.history = history.History()
+ inputargs_and_holes = self.rebuild_state_after_failure(resumedescr)
+ self.history.inputargs = [box for box in inputargs_and_holes if
box]
+ finally:
+ rstack._stack_criticalcode_stop()
def initialize_virtualizable(self, original_boxes):
vinfo = self.jitdriver_sd.virtualizable_info
diff --git a/pypy/jit/metainterp/resume.py b/pypy/jit/metainterp/resume.py
--- a/pypy/jit/metainterp/resume.py
+++ b/pypy/jit/metainterp/resume.py
@@ -6,7 +6,7 @@
from pypy.jit.metainterp import jitprof
from pypy.jit.codewriter.effectinfo import EffectInfo
from pypy.rpython.lltypesystem import lltype, llmemory, rffi, rstr
-from pypy.rlib import rarithmetic
+from pypy.rlib import rarithmetic, rstack
from pypy.rlib.objectmodel import we_are_translated, specialize
from pypy.rlib.debug import have_debug_prints, ll_assert
from pypy.rlib.debug import debug_start, debug_stop, debug_print
@@ -978,12 +978,18 @@
def blackhole_from_resumedata(blackholeinterpbuilder, jitdriver_sd, storage,
all_virtuals=None):
- resumereader = ResumeDataDirectReader(blackholeinterpbuilder.metainterp_sd,
- storage, all_virtuals)
- vinfo = jitdriver_sd.virtualizable_info
- ginfo = jitdriver_sd.greenfield_info
- vrefinfo = blackholeinterpbuilder.metainterp_sd.virtualref_info
- resumereader.consume_vref_and_vable(vrefinfo, vinfo, ginfo)
+ # The initialization is stack-critical code: it must not be interrupted by
+ # StackOverflow, otherwise the jit_virtual_refs are left in a dangling
state.
+ rstack._stack_criticalcode_start()
+ try:
+ resumereader =
ResumeDataDirectReader(blackholeinterpbuilder.metainterp_sd,
+ storage, all_virtuals)
+ vinfo = jitdriver_sd.virtualizable_info
+ ginfo = jitdriver_sd.greenfield_info
+ vrefinfo = blackholeinterpbuilder.metainterp_sd.virtualref_info
+ resumereader.consume_vref_and_vable(vrefinfo, vinfo, ginfo)
+ finally:
+ rstack._stack_criticalcode_stop()
#
# First get a chain of blackhole interpreters whose length is given
# by the depth of rd_frame_info_list. The first one we get must be
diff --git a/pypy/jit/metainterp/test/support.py
b/pypy/jit/metainterp/test/support.py
--- a/pypy/jit/metainterp/test/support.py
+++ b/pypy/jit/metainterp/test/support.py
@@ -26,6 +26,10 @@
def attach_unoptimized_bridge_from_interp(self, greenkey, newloop):
pass
+ def helper_func(self, FUNCPTR, func):
+ from pypy.rpython.annlowlevel import llhelper
+ return llhelper(FUNCPTR, func)
+
def jit_cell_at_key(self, greenkey):
assert greenkey == []
return self._cell
@@ -37,6 +41,7 @@
func._jit_unroll_safe_ = True
rtyper = support.annotate(func, values, type_system=type_system)
graphs = rtyper.annotator.translator.graphs
+ testself.all_graphs = graphs
result_kind = history.getkind(graphs[0].getreturnvar().concretetype)[0]
class FakeJitDriverSD:
diff --git a/pypy/jit/metainterp/test/test_virtualref.py
b/pypy/jit/metainterp/test/test_virtualref.py
--- a/pypy/jit/metainterp/test/test_virtualref.py
+++ b/pypy/jit/metainterp/test/test_virtualref.py
@@ -1,9 +1,10 @@
import py
from pypy.rpython.lltypesystem import lltype, llmemory, lloperation
+from pypy.rpython.llinterp import LLException
from pypy.rlib.jit import JitDriver, dont_look_inside, vref_None
-from pypy.rlib.jit import virtual_ref, virtual_ref_finish
+from pypy.rlib.jit import virtual_ref, virtual_ref_finish, InvalidVirtualRef
from pypy.rlib.objectmodel import compute_unique_id
-from pypy.jit.metainterp.test.support import LLJitMixin, OOJitMixin
+from pypy.jit.metainterp.test.support import LLJitMixin, OOJitMixin,
_get_jitcodes
from pypy.jit.metainterp.resoperation import rop
from pypy.jit.metainterp.virtualref import VirtualRefInfo
@@ -16,6 +17,29 @@
self.vrefinfo = VirtualRefInfo(self.warmrunnerstate)
self.cw.setup_vrefinfo(self.vrefinfo)
+ def test_rewrite_graphs(self):
+ class X:
+ pass
+ def fn():
+ x = X()
+ vref = virtual_ref(x)
+ x1 = vref() # jit_force_virtual
+ virtual_ref_finish(vref, x)
+ #
+ _get_jitcodes(self, self.CPUClass, fn, [], self.type_system)
+ graph = self.all_graphs[0]
+ assert graph.name == 'fn'
+ self.vrefinfo.replace_force_virtual_with_call([graph])
+ #
+ def check_call(op, fname):
+ assert op.opname == 'direct_call'
+ assert op.args[0].value._obj._name == fname
+ #
+ ops = [op for block, op in graph.iterblockops()]
+ check_call(ops[-3], 'virtual_ref')
+ check_call(ops[-2], 'force_virtual_if_necessary')
+ check_call(ops[-1], 'virtual_ref_finish')
+
def test_make_vref_simple(self):
class X:
pass
@@ -25,9 +49,9 @@
#
def f():
x = X()
- exctx.topframeref = virtual_ref(x)
+ exctx.topframeref = vref = virtual_ref(x)
exctx.topframeref = vref_None
- virtual_ref_finish(x)
+ virtual_ref_finish(vref, x)
return 1
#
self.interp_operations(f, [])
@@ -60,8 +84,9 @@
exctx._frame = x
exctx.topframeref = virtual_ref(x)
def leave():
+ vref = exctx.topframeref
exctx.topframeref = vref_None
- virtual_ref_finish(exctx._frame)
+ virtual_ref_finish(vref, exctx._frame)
def f(n):
enter(n)
n = external(n)
@@ -125,7 +150,8 @@
#
@dont_look_inside
def g(vref):
- debug_print(lltype.Void, '-+-+-+-+- external read:', vref().n)
+ # we cannot do anything with the vref after the call to finish()
+ pass
#
def f(n):
while n > 0:
@@ -136,7 +162,7 @@
exctx.topframeref = vref = virtual_ref(x)
# here, 'x' should be virtual
exctx.topframeref = vref_None
- virtual_ref_finish(x)
+ virtual_ref_finish(vref, x)
# 'x' and 'vref' can randomly escape after the call to
# finish().
g(vref)
@@ -144,7 +170,7 @@
return 1
#
self.meta_interp(f, [10])
- self.check_loops(new_with_vtable=2) # the vref and the X
+ self.check_loops(new_with_vtable=1) # the vref
self.check_aborted_count(0)
def test_simple_all_removed(self):
@@ -169,13 +195,13 @@
xy.next1 = lltype.malloc(A, 0)
xy.next2 = lltype.malloc(A, 0)
xy.next3 = lltype.malloc(A, 0)
- exctx.topframeref = virtual_ref(xy)
+ exctx.topframeref = vref = virtual_ref(xy)
n -= externalfn(n)
exctx.topframeref = vref_None
xy.next1 = lltype.nullptr(A)
xy.next2 = lltype.nullptr(A)
xy.next3 = lltype.nullptr(A)
- virtual_ref_finish(xy)
+ virtual_ref_finish(vref, xy)
#
self.meta_interp(f, [15])
self.check_loops(new_with_vtable=0, # all virtualized
@@ -206,17 +232,17 @@
xy.next1 = lltype.malloc(A, 0)
xy.next2 = lltype.malloc(A, 0)
xy.next3 = lltype.malloc(A, 0)
- exctx.topframeref = virtual_ref(xy)
+ exctx.topframeref = vref = virtual_ref(xy)
n -= externalfn(n)
exctx.topframeref = vref_None
xy.next1 = lltype.nullptr(A)
xy.next2 = lltype.nullptr(A)
xy.next3 = lltype.nullptr(A)
- virtual_ref_finish(xy)
+ virtual_ref_finish(vref, xy)
#
self.meta_interp(f, [15])
- self.check_loops(new_with_vtable=2, # the vref, and xy so far,
- new_array=0) # but not xy.next1/2/3
+ self.check_loops(new_with_vtable=1, # the vref: xy doesn't need to
be forced
+ new_array=0) # and neither xy.next1/2/3
self.check_aborted_count(0)
def test_simple_force_always(self):
@@ -244,12 +270,12 @@
xy.next2 = lltype.malloc(A, 0)
xy.next3 = lltype.malloc(A, 0)
xy.n = n
- exctx.topframeref = virtual_ref(xy)
+ exctx.topframeref = vref = virtual_ref(xy)
n -= externalfn(n)
xy.next1 = lltype.nullptr(A)
xy.next2 = lltype.nullptr(A)
xy.next3 = lltype.nullptr(A)
- virtual_ref_finish(xy)
+ virtual_ref_finish(vref, xy)
exctx.topframeref = vref_None
#
self.meta_interp(f, [15])
@@ -282,19 +308,19 @@
xy.next2 = lltype.malloc(A, 0)
xy.next3 = lltype.malloc(A, 0)
xy.n = n
- exctx.topframeref = virtual_ref(xy)
+ exctx.topframeref = vref = virtual_ref(xy)
n -= externalfn(n)
xy.next1 = lltype.nullptr(A)
xy.next2 = lltype.nullptr(A)
xy.next3 = lltype.nullptr(A)
- virtual_ref_finish(xy)
+ virtual_ref_finish(vref, xy)
exctx.topframeref = vref_None
return exctx.m
#
res = self.meta_interp(f, [30])
assert res == 13
- self.check_loops(new_with_vtable=2, # the vref, XY() at the end
- new_array=0) # but not next1/2/3
+ self.check_loops(new_with_vtable=1, # the vref, but not XY()
+ new_array=0) # and neither next1/2/3
self.check_loop_count(1)
self.check_aborted_count(0)
@@ -322,7 +348,7 @@
xy.next2 = lltype.malloc(A, 0)
xy.next3 = lltype.malloc(A, 0)
xy.n = n
- exctx.topframeref = virtual_ref(xy)
+ exctx.topframeref = vref = virtual_ref(xy)
if n == 13:
externalfn(n)
n -= 1
@@ -330,7 +356,7 @@
xy.next1 = lltype.nullptr(A)
xy.next2 = lltype.nullptr(A)
xy.next3 = lltype.nullptr(A)
- virtual_ref_finish(xy)
+ virtual_ref_finish(vref, xy)
return exctx.m
#
res = self.meta_interp(f, [30])
@@ -366,7 +392,7 @@
xy.next4 = lltype.malloc(A, 0)
xy.next5 = lltype.malloc(A, 0)
xy.n = n
- exctx.topframeref = virtual_ref(xy)
+ exctx.topframeref = vref = virtual_ref(xy)
if n % 6 == 0:
xy.next1 = lltype.nullptr(A)
xy.next2 = lltype.nullptr(A)
@@ -379,7 +405,7 @@
xy.next3 = lltype.nullptr(A)
xy.next4 = lltype.nullptr(A)
xy.next5 = lltype.nullptr(A)
- virtual_ref_finish(xy)
+ virtual_ref_finish(vref, xy)
return exctx.m
#
res = self.meta_interp(f, [72])
@@ -389,36 +415,6 @@
new_array=2) # bridge: next4, next5
self.check_aborted_count(0)
- def test_access_vref_later(self):
- myjitdriver = JitDriver(greens = [], reds = ['n'])
- #
- class XY:
- pass
- class ExCtx:
- pass
- exctx = ExCtx()
- #
- @dont_look_inside
- def g():
- return exctx.later().n
- #
- def f(n):
- while n > 0:
- myjitdriver.can_enter_jit(n=n)
- myjitdriver.jit_merge_point(n=n)
- xy = XY()
- xy.n = n
- exctx.topframeref = virtual_ref(xy)
- exctx.later = exctx.topframeref
- n -= 1
- exctx.topframeref = vref_None
- virtual_ref_finish(xy)
- return g()
- #
- res = self.meta_interp(f, [15])
- assert res == 1
- self.check_aborted_count(0)
-
def test_jit_force_virtual_seen(self):
myjitdriver = JitDriver(greens = [], reds = ['n'])
#
@@ -435,12 +431,12 @@
myjitdriver.jit_merge_point(n=n)
xy = XY()
xy.n = n
- exctx.topframeref = virtual_ref(xy)
+ exctx.topframeref = vref = virtual_ref(xy)
xy.next1 = lltype.malloc(A, 0)
n = exctx.topframeref().n - 1
xy.next1 = lltype.nullptr(A)
exctx.topframeref = vref_None
- virtual_ref_finish(xy)
+ virtual_ref_finish(vref, xy)
return 1
#
res = self.meta_interp(f, [15])
@@ -465,12 +461,12 @@
if reclevel == 0:
return n
xy = XY()
- exctx.topframeref = virtual_ref(xy)
+ exctx.topframeref = vref = virtual_ref(xy)
m = f(xy, n, reclevel-1)
assert m == n
n -= 1
exctx.topframeref = vref_None
- virtual_ref_finish(xy)
+ virtual_ref_finish(vref, xy)
return 2
def main(n, reclevel):
return f(XY(), n, reclevel)
@@ -495,7 +491,7 @@
frame.n += 1
xy = XY()
xy.n = n
- exctx.topframeref = virtual_ref(xy)
+ exctx.topframeref = vref = virtual_ref(xy)
if reclevel > 0:
m = f(xy, frame.n, reclevel-1)
assert xy.n == m
@@ -503,7 +499,7 @@
else:
n -= 2
exctx.topframeref = vref_None
- virtual_ref_finish(xy)
+ virtual_ref_finish(vref, xy)
return frame.n
def main(n, reclevel):
return f(XY(), n, reclevel)
@@ -540,7 +536,7 @@
escapexy(xy)
# clean up
exctx.vr = vref_None
- virtual_ref_finish(xy)
+ virtual_ref_finish(vr, xy)
n -= 1
return 1
#
@@ -548,6 +544,57 @@
assert res == 1
self.check_loops(new_with_vtable=2) # vref, xy
+ def test_cannot_use_invalid_virtualref(self):
+ myjitdriver = JitDriver(greens = [], reds = ['n'])
+ #
+ class XY:
+ n = 0
+ #
+ def fn(n):
+ res = False
+ while n > 0:
+ myjitdriver.can_enter_jit(n=n)
+ myjitdriver.jit_merge_point(n=n)
+ xy = XY()
+ xy.n = n
+ vref = virtual_ref(xy)
+ virtual_ref_finish(vref, xy)
+ vref() # raises InvalidVirtualRef when jitted
+ n -= 1
+ return res
+ #
+ py.test.raises(InvalidVirtualRef, "fn(10)")
+ py.test.raises(LLException, "self.meta_interp(fn, [10])")
+
+ def test_call_virtualref_already_forced(self):
+ myjitdriver = JitDriver(greens = [], reds = ['n', 'res'])
+ #
+ class XY:
+ n = 0
+ #
+ @dont_look_inside
+ def force_it(vref, n):
+ if n % 6 == 0:
+ return vref().n
+ return 0
+ def fn(n):
+ res = 0
+ while n > 0:
+ myjitdriver.can_enter_jit(n=n, res=res)
+ myjitdriver.jit_merge_point(n=n, res=res)
+ xy = XY()
+ xy.n = n
+ vref = virtual_ref(xy)
+ force_it(vref, n)
+ virtual_ref_finish(vref, xy)
+ res += force_it(vref, n) # doesn't raise, because it was
already forced
+ n -= 1
+ return res
+ #
+ assert fn(10) == 6
+ res = self.meta_interp(fn, [10])
+ assert res == 6
+
class TestLLtype(VRefTests, LLJitMixin):
pass
diff --git a/pypy/jit/metainterp/virtualref.py
b/pypy/jit/metainterp/virtualref.py
--- a/pypy/jit/metainterp/virtualref.py
+++ b/pypy/jit/metainterp/virtualref.py
@@ -2,7 +2,7 @@
from pypy.rpython.lltypesystem import lltype, llmemory, rffi, rclass
from pypy.jit.metainterp import history
from pypy.jit.codewriter import heaptracker
-
+from pypy.rlib.jit import InvalidVirtualRef
class VirtualRefInfo:
@@ -38,23 +38,24 @@
def replace_force_virtual_with_call(self, graphs):
# similar to rvirtualizable2.replace_force_virtualizable_with_call().
- c_funcptr = None
- count = 0
+ c_force_virtual_ptr = None
+ force_virtual_count = 0
for graph in graphs:
for block in graph.iterblocks():
for op in block.operations:
if op.opname == 'jit_force_virtual':
# first compute c_funcptr, but only if there is any
# 'jit_force_virtual' around
- if c_funcptr is None:
- c_funcptr = self.get_force_virtual_fnptr()
+ if c_force_virtual_ptr is None:
+ c_force_virtual_ptr =
self.get_force_virtual_fnptr()
#
op.opname = 'direct_call'
- op.args = [c_funcptr, op.args[0]]
- count += 1
- if c_funcptr is not None:
- log("replaced %d 'jit_force_virtual' with %r" % (count,
- c_funcptr.value))
+ op.args = [c_force_virtual_ptr, op.args[0]]
+ force_virtual_count += 1
+ #
+ if c_force_virtual_ptr is not None:
+ log("replaced %d 'jit_force_virtual' with %r" %
(force_virtual_count,
+
c_force_virtual_ptr.value))
# ____________________________________________________________
@@ -145,7 +146,8 @@
ResumeGuardForcedDescr.force_now(self.cpu, token)
assert vref.virtual_token == self.TOKEN_NONE
assert vref.forced
- else:
- assert vref.forced
+ elif not vref.forced:
+ # token == TOKEN_NONE and the vref was not forced: it's invalid
+ raise InvalidVirtualRef
return vref.forced
force_virtual._dont_inline_ = True
diff --git a/pypy/module/_rawffi/callback.py b/pypy/module/_rawffi/callback.py
--- a/pypy/module/_rawffi/callback.py
+++ b/pypy/module/_rawffi/callback.py
@@ -43,7 +43,7 @@
unwrap_value(space, push_elem, ll_res, 0,
callback_ptr.result, w_res)
except OperationError, e:
- tbprint(space, space.wrap(e.application_traceback),
+ tbprint(space, space.wrap(e.get_traceback()),
space.wrap(e.errorstr(space)))
# force the result to be zero
if callback_ptr.result is not None:
diff --git a/pypy/module/_stackless/interp_coroutine.py
b/pypy/module/_stackless/interp_coroutine.py
--- a/pypy/module/_stackless/interp_coroutine.py
+++ b/pypy/module/_stackless/interp_coroutine.py
@@ -125,7 +125,7 @@
if isinstance(operror, OperationError):
w_exctype = operror.w_type
w_excvalue = operror.get_w_value(space)
- w_exctraceback = operror.application_traceback
+ w_exctraceback = operror.get_traceback()
w_excinfo = space.newtuple([w_exctype, w_excvalue, w_exctraceback])
if w_exctype is self.costate.w_CoroutineExit:
@@ -160,7 +160,7 @@
space.gettypeobject(pytraceback.PyTraceback.typedef))):
raise OperationError(space.w_TypeError,
space.wrap("throw: arg 3 must be a traceback or None"))
- operror.application_traceback = tb
+ operror.set_traceback(tb)
self._kill(operror)
diff --git a/pypy/module/_stackless/interp_greenlet.py
b/pypy/module/_stackless/interp_greenlet.py
--- a/pypy/module/_stackless/interp_greenlet.py
+++ b/pypy/module/_stackless/interp_greenlet.py
@@ -124,7 +124,7 @@
space.gettypeobject(pytraceback.PyTraceback.typedef))):
raise OperationError(space.w_TypeError,
space.wrap("throw: arg 3 must be a traceback or None"))
- operror.application_traceback = tb
+ operror.set_traceback(tb)
# Dead greenlet: turn GreenletExit into a regular return
if self.isdead() and operror.match(space, self.costate.w_GreenletExit):
args_w = [operror.get_w_value(space)]
diff --git a/pypy/module/cpyext/pyerrors.py b/pypy/module/cpyext/pyerrors.py
--- a/pypy/module/cpyext/pyerrors.py
+++ b/pypy/module/cpyext/pyerrors.py
@@ -57,7 +57,7 @@
if operror:
ptype[0] = make_ref(space, operror.w_type)
pvalue[0] = make_ref(space, operror.get_w_value(space))
- ptraceback[0] = make_ref(space,
space.wrap(operror.application_traceback))
+ ptraceback[0] = make_ref(space, space.wrap(operror.get_traceback()))
else:
ptype[0] = lltype.nullptr(PyObject.TO)
pvalue[0] = lltype.nullptr(PyObject.TO)
@@ -268,7 +268,7 @@
w_type = operror.w_type
w_value = operror.get_w_value(space)
- w_tb = space.wrap(operror.application_traceback)
+ w_tb = space.wrap(operror.get_traceback())
if rffi.cast(lltype.Signed, set_sys_last_vars):
space.sys.setdictvalue(space, "last_type", w_type)
diff --git a/pypy/module/sys/__init__.py b/pypy/module/sys/__init__.py
--- a/pypy/module/sys/__init__.py
+++ b/pypy/module/sys/__init__.py
@@ -150,7 +150,7 @@
if operror is None:
return space.w_None
else:
- return space.wrap(operror.application_traceback)
+ return space.wrap(operror.get_traceback())
return None
def get_w_default_encoder(self):
diff --git a/pypy/module/sys/vm.py b/pypy/module/sys/vm.py
--- a/pypy/module/sys/vm.py
+++ b/pypy/module/sys/vm.py
@@ -40,6 +40,7 @@
break
depth -= 1
f = ec.getnextframe_nohidden(f)
+ f.mark_as_escaped()
return space.wrap(f)
def setrecursionlimit(space, w_new_limit):
@@ -90,7 +91,7 @@
return space.newtuple([space.w_None,space.w_None,space.w_None])
else:
return space.newtuple([operror.w_type, operror.get_w_value(space),
- space.wrap(operror.application_traceback)])
+ space.wrap(operror.get_traceback())])
def exc_clear(space):
"""Clear global information on the current exception. Subsequent calls
diff --git a/pypy/objspace/trace.py b/pypy/objspace/trace.py
--- a/pypy/objspace/trace.py
+++ b/pypy/objspace/trace.py
@@ -110,10 +110,10 @@
self.result.append(EnterFrame(frame))
self.ec.enter(frame)
- def leave(self, frame, w_exitvalue):
+ def leave(self, frame, w_exitvalue, got_exception):
""" called just after evaluating of a frame is suspended/finished. """
self.result.append(LeaveFrame(frame))
- self.ec.leave(frame, w_exitvalue)
+ self.ec.leave(frame, w_exitvalue, got_exception)
def bytecode_trace(self, frame):
""" called just before execution of a bytecode. """
diff --git a/pypy/rlib/_jit_vref.py b/pypy/rlib/_jit_vref.py
--- a/pypy/rlib/_jit_vref.py
+++ b/pypy/rlib/_jit_vref.py
@@ -50,6 +50,7 @@
def rtype_simple_call(self, hop):
[v] = hop.inputargs(self)
+ hop.exception_is_here()
v = hop.genop('jit_force_virtual', [v], resulttype = OBJECTPTR)
return hop.genop('cast_pointer', [v], resulttype = hop.r_result)
@@ -65,6 +66,7 @@
lowleveltype = OBJECT
def rtype_simple_call(self, hop):
[v] = hop.inputargs(self)
+ hop.exception_is_here()
v = hop.genop('jit_force_virtual', [v], resulttype = OBJECT)
return hop.genop('oodowncast', [v], resulttype = hop.r_result)
diff --git a/pypy/rlib/jit.py b/pypy/rlib/jit.py
--- a/pypy/rlib/jit.py
+++ b/pypy/rlib/jit.py
@@ -183,7 +183,6 @@
# VRefs
def virtual_ref(x):
-
"""Creates a 'vref' object that contains a reference to 'x'. Calls
to virtual_ref/virtual_ref_finish must be properly nested. The idea
is that the object 'x' is supposed to be JITted as a virtual between
@@ -194,10 +193,10 @@
return DirectJitVRef(x)
virtual_ref.oopspec = 'virtual_ref(x)'
-def virtual_ref_finish(x):
- """See docstring in virtual_ref(x). Note that virtual_ref_finish
- takes as argument the real object, not the vref."""
+def virtual_ref_finish(vref, x):
+ """See docstring in virtual_ref(x)"""
keepalive_until_here(x) # otherwise the whole function call is removed
+ _virtual_ref_finish(vref, x)
virtual_ref_finish.oopspec = 'virtual_ref_finish(x)'
def non_virtual_ref(x):
@@ -205,19 +204,39 @@
Used for None or for frames outside JIT scope."""
return DirectVRef(x)
+class InvalidVirtualRef(Exception):
+ """
+ Raised if we try to call a non-forced virtualref after the call to
+ virtual_ref_finish
+ """
+
# ---------- implementation-specific ----------
class DirectVRef(object):
def __init__(self, x):
self._x = x
+ self._state = 'non-forced'
+
def __call__(self):
+ if self._state == 'non-forced':
+ self._state = 'forced'
+ elif self._state == 'invalid':
+ raise InvalidVirtualRef
return self._x
+ def _finish(self):
+ if self._state == 'non-forced':
+ self._state = 'invalid'
+
class DirectJitVRef(DirectVRef):
def __init__(self, x):
assert x is not None, "virtual_ref(None) is not allowed"
DirectVRef.__init__(self, x)
+def _virtual_ref_finish(vref, x):
+ assert vref._x is x, "Invalid call to virtual_ref_finish"
+ vref._finish()
+
class Entry(ExtRegistryEntry):
_about_ = (non_virtual_ref, DirectJitVRef)
@@ -237,6 +256,15 @@
s_obj = self.bookkeeper.immutablevalue(self.instance())
return _jit_vref.SomeVRef(s_obj)
+class Entry(ExtRegistryEntry):
+ _about_ = _virtual_ref_finish
+
+ def compute_result_annotation(self, s_vref, s_obj):
+ pass
+
+ def specialize_call(self, hop):
+ pass
+
vref_None = non_virtual_ref(None)
# ____________________________________________________________
diff --git a/pypy/rlib/rstack.py b/pypy/rlib/rstack.py
--- a/pypy/rlib/rstack.py
+++ b/pypy/rlib/rstack.py
@@ -56,6 +56,12 @@
_stack_get_end_adr = llexternal('LL_stack_get_end_adr', [], lltype.Signed)
_stack_get_length_adr= llexternal('LL_stack_get_length_adr',[], lltype.Signed)
+# the following is also used by the JIT: "critical code" paths are paths in
+# which we should not raise StackOverflow at all, but just ignore the stack
limit
+_stack_criticalcode_start = llexternal('LL_stack_criticalcode_start', [],
+ lltype.Void, lambda: None)
+_stack_criticalcode_stop = llexternal('LL_stack_criticalcode_stop', [],
+ lltype.Void, lambda: None)
def stack_check():
if not we_are_translated():
diff --git a/pypy/rlib/test/test__jit_vref.py b/pypy/rlib/test/test__jit_vref.py
--- a/pypy/rlib/test/test__jit_vref.py
+++ b/pypy/rlib/test/test__jit_vref.py
@@ -1,6 +1,6 @@
import py
from pypy.rlib.jit import virtual_ref, virtual_ref_finish
-from pypy.rlib.jit import vref_None, non_virtual_ref
+from pypy.rlib.jit import vref_None, non_virtual_ref, InvalidVirtualRef
from pypy.rlib._jit_vref import SomeVRef
from pypy.annotation import model as annmodel
from pypy.annotation.annrpython import RPythonAnnotator
@@ -23,18 +23,23 @@
pass
-def test_direct_1():
+def test_direct_forced():
x1 = X()
vref = virtual_ref(x1)
+ assert vref._state == 'non-forced'
assert vref() is x1
- virtual_ref_finish(x1)
+ assert vref._state == 'forced'
+ virtual_ref_finish(vref, x1)
+ assert vref._state == 'forced'
assert vref() is x1
-def test_direct_2():
+def test_direct_invalid():
x1 = X()
vref = virtual_ref(x1)
- virtual_ref_finish(x1)
- assert vref() is x1
+ assert vref._state == 'non-forced'
+ virtual_ref_finish(vref, x1)
+ assert vref._state == 'invalid'
+ py.test.raises(InvalidVirtualRef, "vref()")
def test_annotate_1():
def f():
@@ -50,7 +55,7 @@
x1 = X()
vref = virtual_ref(x1)
x2 = vref()
- virtual_ref_finish(x1)
+ virtual_ref_finish(vref, x1)
return x2
a = RPythonAnnotator()
s = a.build_types(f, [])
@@ -95,7 +100,7 @@
x1 = X()
vref = virtual_ref(x1)
x2 = vref()
- virtual_ref_finish(x2)
+ virtual_ref_finish(vref, x2)
return x2
x = self.interpret(f, [])
assert self.castable(self.OBJECTTYPE, x)
@@ -119,6 +124,18 @@
assert lltype.typeOf(x) == self.OBJECTTYPE
assert not x
+ def test_rtype_5(self):
+ def f():
+ vref = virtual_ref(X())
+ try:
+ vref()
+ return 42
+ except InvalidVirtualRef:
+ return -1
+ x = self.interpret(f, [])
+ assert x == 42
+
+
class TestLLtype(BaseTestVRef, LLRtypeMixin):
OBJECTTYPE = OBJECTPTR
def castable(self, TO, var):
diff --git a/pypy/tool/pytest/appsupport.py b/pypy/tool/pytest/appsupport.py
--- a/pypy/tool/pytest/appsupport.py
+++ b/pypy/tool/pytest/appsupport.py
@@ -81,7 +81,7 @@
self.space = space
self.operr = operr
self.typename = operr.w_type.getname(space, "?")
- self.traceback = AppTraceback(space, self.operr.application_traceback)
+ self.traceback = AppTraceback(space, self.operr.get_traceback())
debug_excs = getattr(operr, 'debug_excs', [])
if debug_excs:
self._excinfo = debug_excs[0]
diff --git a/pypy/translator/c/src/debug_traceback.h
b/pypy/translator/c/src/debug_traceback.h
--- a/pypy/translator/c/src/debug_traceback.h
+++ b/pypy/translator/c/src/debug_traceback.h
@@ -21,7 +21,11 @@
line to the f:17/KeyError line.
*/
-#define PYPY_DEBUG_TRACEBACK_DEPTH 128 /* a power of two */
+#ifdef RPY_LL_ASSERT
+# define PYPY_DEBUG_TRACEBACK_DEPTH 8192 /* a power of two */
+#else
+# define PYPY_DEBUG_TRACEBACK_DEPTH 128 /* a power of two */
+#endif
#define PYPYDTPOS_RERAISE ((struct pypydtpos_s *) -1)
#define PYPYDTSTORE(loc, etype) \
diff --git a/pypy/translator/c/src/stack.h b/pypy/translator/c/src/stack.h
--- a/pypy/translator/c/src/stack.h
+++ b/pypy/translator/c/src/stack.h
@@ -13,6 +13,7 @@
extern char *_LLstacktoobig_stack_end;
extern long _LLstacktoobig_stack_length;
+extern char _LLstacktoobig_report_error;
void LL_stack_unwind(void);
char LL_stack_too_big_slowpath(long); /* returns 0 (ok) or 1 (too big) */
@@ -24,6 +25,9 @@
#define LL_stack_get_end_adr() ((long)&_LLstacktoobig_stack_end) /* JIT */
#define LL_stack_get_length_adr() ((long)&_LLstacktoobig_stack_length)/* JIT */
+#define LL_stack_criticalcode_start() (_LLstacktoobig_report_error = 0)
+#define LL_stack_criticalcode_stop() (_LLstacktoobig_report_error = 1)
+
#ifdef __GNUC__
# define PYPY_INHIBIT_TAIL_CALL() asm("/* inhibit_tail_call */")
@@ -39,6 +43,7 @@
stack that grows downward here. */
char *_LLstacktoobig_stack_end = NULL;
long _LLstacktoobig_stack_length = MAX_STACK_SIZE;
+char _LLstacktoobig_report_error = 1;
static RPyThreadStaticTLS end_tls_key;
void LL_stack_set_length_fraction(double fraction)
@@ -86,8 +91,9 @@
/* stack underflowed: the initial estimation of
the stack base must be revised */
}
- else
- return 1; /* stack overflow (probably) */
+ else { /* stack overflow (probably) */
+ return _LLstacktoobig_report_error;
+ }
}
/* update the stack base pointer to the current value */
diff --git a/pypy/translator/c/test/test_standalone.py
b/pypy/translator/c/test/test_standalone.py
--- a/pypy/translator/c/test/test_standalone.py
+++ b/pypy/translator/c/test/test_standalone.py
@@ -727,6 +727,40 @@
assert counts[0.1] > counts[0.4] / 7
assert counts[0.4] > counts[1.0] / 4
+ def test_stack_criticalcode(self):
+ # check for pypy.rlib.rstack._stack_criticalcode_start/stop()
+ from pypy.rlib.rstack import _stack_criticalcode_start
+ from pypy.rlib.rstack import _stack_criticalcode_stop
+ from pypy.rlib.rstackovf import StackOverflow
+ class A:
+ pass
+ glob = A()
+ def f(n):
+ if n <= 0:
+ return 42
+ try:
+ return f(n+1)
+ except StackOverflow:
+ if glob.caught:
+ print 'Oups! already caught!'
+ glob.caught = True
+ _stack_criticalcode_start()
+ critical(100) # recurse another 100 times here
+ _stack_criticalcode_stop()
+ return 789
+ def critical(n):
+ if n > 0:
+ n = critical(n - 1)
+ return n - 42
+ def entry_point(argv):
+ glob.caught = False
+ print f(1)
+ return 0
+ t, cbuilder = self.compile(entry_point, stackcheck=True)
+ out = cbuilder.cmdexec('')
+ assert out.strip() == '789'
+
+
class TestMaemo(TestStandalone):
def setup_class(cls):
py.test.skip("TestMaemo: tests skipped for now")
diff --git a/pypy/translator/transform.py b/pypy/translator/transform.py
--- a/pypy/translator/transform.py
+++ b/pypy/translator/transform.py
@@ -175,41 +175,6 @@
# make sure the bookkeeper knows about AssertionError
self.bookkeeper.getuniqueclassdef(AssertionError)
-def insert_stackcheck(ann):
- from pypy.tool.algo.graphlib import Edge, make_edge_dict, break_cycles
- edges = []
- graphs_to_patch = {}
- for callposition, (caller, callee) in ann.translator.callgraph.items():
- if getattr(getattr(callee, 'func', None), 'insert_stack_check_here',
False):
- graphs_to_patch[callee] = True
- continue
- edge = Edge(caller, callee)
- edge.callposition = callposition
- edges.append(edge)
-
- for graph in graphs_to_patch:
- v = Variable()
- ann.setbinding(v, annmodel.SomeImpossibleValue())
- unwind_op = SpaceOperation('simple_call', [Constant(stack_check)], v)
- graph.startblock.operations.insert(0, unwind_op)
-
- edgedict = make_edge_dict(edges)
- for edge in break_cycles(edgedict, edgedict):
- caller = edge.source
- _, _, call_tag = edge.callposition
- if call_tag:
- caller_block, _ = call_tag
- else:
- ann.warning("cycle detected but no information on where to insert "
- "stack_check()")
- continue
- # caller block found, insert stack_check()
- v = Variable()
- # push annotation on v
- ann.setbinding(v, annmodel.SomeImpossibleValue())
- unwind_op = SpaceOperation('simple_call', [Constant(stack_check)], v)
- caller_block.operations.insert(0, unwind_op)
-
def insert_ll_stackcheck(translator):
from pypy.translator.backendopt.support import find_calls_from
from pypy.rlib.rstack import stack_check
_______________________________________________
pypy-commit mailing list
[email protected]
http://mail.python.org/mailman/listinfo/pypy-commit