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

Reply via email to