Author: Armin Rigo <[email protected]>
Branch:
Changeset: r80137:3d1ee9e224b8
Date: 2015-10-12 16:55 +0200
http://bitbucket.org/pypy/pypy/changeset/3d1ee9e224b8/
Log: Hopefully fix the BridgeExceptionNotFirst problem
diff --git a/rpython/jit/backend/llgraph/runner.py
b/rpython/jit/backend/llgraph/runner.py
--- a/rpython/jit/backend/llgraph/runner.py
+++ b/rpython/jit/backend/llgraph/runner.py
@@ -45,8 +45,6 @@
# we don't care about the value 13 here, because we gonna
# fish it from the extra slot on frame anyway
op.getdescr().make_a_counter_per_value(op, 13)
- elif opnum == rop.BRIDGE_EXCEPTION:
- assert len(self.operations) == 0 # must be first
if op.getdescr() is not None:
if op.is_guard() or op.getopnum() == rop.FINISH:
newdescr = op.getdescr()
@@ -906,8 +904,8 @@
values.append(value)
if hasattr(descr, '_llgraph_bridge'):
if propagate_exception:
- assert (descr._llgraph_bridge.operations[0].opnum ==
- rop.BRIDGE_EXCEPTION)
+ assert (descr._llgraph_bridge.operations[0].opnum in
+ (rop.SAVE_EXC_CLASS, rop.GUARD_EXCEPTION))
target = (descr._llgraph_bridge, -1)
values = [value for value in values if value is not None]
raise Jump(target, values)
@@ -1229,8 +1227,32 @@
def execute_keepalive(self, descr, x):
pass
- def execute_bridge_exception(self, descr):
- pass
+ def execute_save_exc_class(self, descr):
+ lle = self.last_exception
+ if lle is None:
+ return 0
+ else:
+ return support.cast_to_int(lle.args[0])
+
+ def execute_save_exception(self, descr):
+ lle = self.last_exception
+ if lle is None:
+ res = lltype.nullptr(llmemory.GCREF.TO)
+ else:
+ res = lltype.cast_opaque_ptr(llmemory.GCREF, lle.args[1])
+ self.last_exception = None
+ return res
+
+ def execute_restore_exception(self, descr, kls, e):
+ kls = heaptracker.int2adr(kls)
+ if e:
+ value = lltype.cast_opaque_ptr(rclass.OBJECTPTR, e)
+ assert llmemory.cast_ptr_to_adr(value.typeptr) == kls
+ lle = LLException(value.typeptr, e)
+ else:
+ assert kls == llmemory.NULL
+ lle = None
+ self.last_exception = lle
def _getdescr(op):
diff --git a/rpython/jit/backend/llsupport/rewrite.py
b/rpython/jit/backend/llsupport/rewrite.py
--- a/rpython/jit/backend/llsupport/rewrite.py
+++ b/rpython/jit/backend/llsupport/rewrite.py
@@ -119,6 +119,7 @@
# barriers. We do this on each "basic block" of operations, which in
# this case means between CALLs or unknown-size mallocs.
#
+ operations = self.remove_bridge_exception(operations)
for i in range(len(operations)):
op = operations[i]
assert op.get_forwarded() is None
@@ -168,9 +169,6 @@
continue
if op.getopnum() == rop.JUMP or op.getopnum() == rop.FINISH:
self.emit_pending_zeros()
- if op.getopnum() == rop.BRIDGE_EXCEPTION:
- self.remove_bridge_exception(operations, i)
- continue
#
self.emit_op(op)
return self._newops
@@ -686,13 +684,17 @@
size = max(size, 2 * WORD)
return (size + WORD-1) & ~(WORD-1) # round up
- def remove_bridge_exception(self, operations, i):
- """Check that the 'bridge_exception' operation occurs at the
- start of the bridge."""
- if i == 0:
- return # first operation, ok
- if i == 1 and operations[0].getopnum() == rop.INCREMENT_DEBUG_COUNTER:
- return # 2nd operation after INCREMENT_DEBUG_COUNTER, ok
- # not ok!
- assert we_are_translated()
- raise BridgeExceptionNotFirst
+ def remove_bridge_exception(self, operations):
+ """Check a common case: 'save_exception' immediately followed by
+ 'restore_exception' at the start of the bridge."""
+ # XXX should check if the boxes are used later; but we just assume
+ # they aren't for now
+ start = 0
+ if operations[0].getopnum() == rop.INCREMENT_DEBUG_COUNTER:
+ start = 1
+ if len(operations) >= start + 3:
+ if (operations[start+0].getopnum() == rop.SAVE_EXC_CLASS and
+ operations[start+1].getopnum() == rop.SAVE_EXCEPTION and
+ operations[start+2].getopnum() == rop.RESTORE_EXCEPTION):
+ return operations[:start] + operations[start+3:]
+ return operations
diff --git a/rpython/jit/backend/test/runner_test.py
b/rpython/jit/backend/test/runner_test.py
--- a/rpython/jit/backend/test/runner_test.py
+++ b/rpython/jit/backend/test/runner_test.py
@@ -2099,6 +2099,60 @@
excvalue = self.cpu.grab_exc_value(deadframe)
assert not excvalue
+ def test_save_restore_exceptions(self):
+ exc_tp = None
+ exc_ptr = None
+ def func(i):
+ if hasattr(self.cpu, '_exception_emulator'):
+ assert not self.cpu._exception_emulator[0]
+ assert not self.cpu._exception_emulator[1]
+ called.append(i)
+ if i:
+ raise LLException(exc_tp, exc_ptr)
+
+ ops = '''
+ [i0]
+ i1 = same_as_i(1)
+ call_n(ConstClass(fptr), i0, descr=calldescr)
+ i2 = save_exc_class()
+ p2 = save_exception()
+ call_n(ConstClass(fptr), 0, descr=calldescr)
+ restore_exception(i2, p2)
+ p0 = guard_exception(ConstClass(xtp)) [i1]
+ finish(p0)
+ '''
+ FPTR = lltype.Ptr(lltype.FuncType([lltype.Signed], lltype.Void))
+ fptr = llhelper(FPTR, func)
+ calldescr = self.cpu.calldescrof(FPTR.TO, FPTR.TO.ARGS, FPTR.TO.RESULT,
+ EffectInfo.MOST_GENERAL)
+
+ xtp = lltype.malloc(rclass.OBJECT_VTABLE, immortal=True)
+ xtp.subclassrange_min = 1
+ xtp.subclassrange_max = 3
+ X = lltype.GcStruct('X', ('parent', rclass.OBJECT),
+ hints={'vtable': xtp._obj})
+ xx = lltype.malloc(X)
+ xx.parent.typeptr = xtp
+ xptr = lltype.cast_opaque_ptr(llmemory.GCREF, xx)
+
+ exc_tp = xtp
+ exc_ptr = xptr
+ loop = parse(ops, self.cpu, namespace=locals())
+ looptoken = JitCellToken()
+ self.cpu.compile_loop(loop.inputargs, loop.operations, looptoken)
+ called = []
+ deadframe = self.cpu.execute_token(looptoken, 5)
+ assert called == [5, 0]
+ assert self.cpu.get_ref_value(deadframe, 0) == xptr
+ excvalue = self.cpu.grab_exc_value(deadframe)
+ assert not excvalue
+ called = []
+ deadframe = self.cpu.execute_token(looptoken, 0)
+ assert called == [0, 0]
+ assert self.cpu.get_int_value(deadframe, 0) == 1
+ excvalue = self.cpu.grab_exc_value(deadframe)
+ assert not excvalue
+
def test_cond_call_gc_wb(self):
def func_void(a):
record.append(rffi.cast(lltype.Signed, a))
diff --git a/rpython/jit/backend/x86/assembler.py
b/rpython/jit/backend/x86/assembler.py
--- a/rpython/jit/backend/x86/assembler.py
+++ b/rpython/jit/backend/x86/assembler.py
@@ -1609,6 +1609,15 @@
self.implement_guard(guard_token)
self._store_and_reset_exception(self.mc, resloc)
+ def genop_save_exc_class(self, op, arglocs, resloc):
+ self.mc.MOV(resloc, heap(self.cpu.pos_exception()))
+
+ def genop_save_exception(self, op, arglocs, resloc):
+ self._store_and_reset_exception(self.mc, resloc)
+
+ def genop_discard_restore_exception(self, op, arglocs):
+ self._restore_exception(self.mc, arglocs[1], arglocs[0])
+
def _store_and_reset_exception(self, mc, excvalloc=None, exctploc=None,
tmploc=None):
""" Resest the exception. If excvalloc is None, then store it on the
diff --git a/rpython/jit/backend/x86/regalloc.py
b/rpython/jit/backend/x86/regalloc.py
--- a/rpython/jit/backend/x86/regalloc.py
+++ b/rpython/jit/backend/x86/regalloc.py
@@ -418,6 +418,17 @@
self.perform_guard(op, [loc, loc1], resloc)
self.rm.possibly_free_var(box)
+ def consider_save_exception(self, op):
+ resloc = self.rm.force_allocate_reg(op)
+ self.perform(op, [], resloc)
+ consider_save_exc_class = consider_save_exception
+
+ def consider_restore_exception(self, op):
+ args = op.getarglist()
+ loc0 = self.rm.make_sure_var_in_reg(op.getarg(0), args) # exc class
+ loc1 = self.rm.make_sure_var_in_reg(op.getarg(1), args) # exc instance
+ self.perform_discard(op, [loc0, loc1])
+
consider_guard_no_overflow = consider_guard_no_exception
consider_guard_overflow = consider_guard_no_exception
consider_guard_not_forced = consider_guard_no_exception
diff --git a/rpython/jit/metainterp/executor.py
b/rpython/jit/metainterp/executor.py
--- a/rpython/jit/metainterp/executor.py
+++ b/rpython/jit/metainterp/executor.py
@@ -386,7 +386,9 @@
rop.CALL_MALLOC_NURSERY_VARSIZE_FRAME,
rop.NURSERY_PTR_INCREMENT,
rop.LABEL,
- rop.BRIDGE_EXCEPTION,
+ rop.SAVE_EXC_CLASS,
+ rop.SAVE_EXCEPTION,
+ rop.RESTORE_EXCEPTION,
): # list of opcodes never executed by pyjitpl
continue
raise AssertionError("missing %r" % (key,))
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
@@ -2487,17 +2487,28 @@
# 'test_guard_no_exception_incorrectly_removed_from_bridge'
# shows a corner case in which just putting GuARD_NO_EXCEPTION
# here is a bad idea: the optimizer might remove it too.
- # So we put a pair BRIDGE_EXCEPTION / GUARD_(NO)_EXCEPTION.
- # The BRIDGE_EXCEPTION is meant to re-raise the exception
- # caught before the bridge, but in reality it must end up
- # as the first operation and thus is a no-op for the backends
- # (it is removed in rewrite.py). Its real purpose is only to
- # pass through the optimizer unmodified, so that the following
- # GUARD_NO_EXCEPTION is not killed.
- self.history.record(rop.BRIDGE_EXCEPTION, [], None)
- if exception:
- self.execute_ll_raised(lltype.cast_opaque_ptr(rclass.OBJECTPTR,
- exception))
+ # So we put a SAVE_EXCEPTION at the start, and a
+ # RESTORE_EXCEPTION just before the guard. (rewrite.py will
+ # remove the two if they end up consecutive.)
+
+ # XXX too much jumps between older and newer models; clean up
+ # by killing SAVE_EXC_CLASS, RESTORE_EXCEPTION and GUARD_EXCEPTION
+
+ exception_obj = lltype.cast_opaque_ptr(rclass.OBJECTPTR, exception)
+ if exception_obj:
+ exc_class = heaptracker.adr2int(
+ llmemory.cast_ptr_to_adr(exception_obj.typeptr))
+ else:
+ exc_class = 0
+ i = len(self.history.operations)
+ op1 = self.history.record(rop.SAVE_EXC_CLASS, [], exc_class)
+ op2 = self.history.record(rop.SAVE_EXCEPTION, [], exception)
+ assert op1 is self.history.operations[i]
+ assert op2 is self.history.operations[i + 1]
+ self.history.operations = [op1, op2] + self.history.operations[:i]
+ self.history.record(rop.RESTORE_EXCEPTION, [op1, op2], None)
+ if exception_obj:
+ self.execute_ll_raised(exception_obj)
else:
self.clear_exception()
try:
diff --git a/rpython/jit/metainterp/resoperation.py
b/rpython/jit/metainterp/resoperation.py
--- a/rpython/jit/metainterp/resoperation.py
+++ b/rpython/jit/metainterp/resoperation.py
@@ -696,7 +696,7 @@
'GUARD_SUBCLASS/2d/n', # only if supports_guard_gc_type
'_GUARD_FOLDABLE_LAST',
'GUARD_NO_EXCEPTION/0d/n', # may be called with an exception currently
set
- 'GUARD_EXCEPTION/1d/r', # may be called with an exception currently set
+ 'GUARD_EXCEPTION/1d/r', # XXX kill me, use only SAVE_EXCEPTION
'GUARD_NO_OVERFLOW/0d/n',
'GUARD_OVERFLOW/0d/n',
'GUARD_NOT_FORCED/0d/n', # may be called with an exception currently
set
@@ -827,7 +827,9 @@
'QUASIIMMUT_FIELD/1d/n', # [objptr], descr=SlowMutateDescr
'RECORD_EXACT_CLASS/2/n', # [objptr, clsptr]
'KEEPALIVE/1/n',
- 'BRIDGE_EXCEPTION/0/n', # pyjitpl: prepare_resume_from_failure()
+ 'SAVE_EXCEPTION/0/r',
+ 'SAVE_EXC_CLASS/0/i', # XXX kill me
+ 'RESTORE_EXCEPTION/2/n', # XXX kill me
'_CANRAISE_FIRST', # ----- start of can_raise operations -----
'_CALL_FIRST',
_______________________________________________
pypy-commit mailing list
[email protected]
https://mail.python.org/mailman/listinfo/pypy-commit