[pypy-svn] r69957 - in pypy/branch/virtual-forcing/pypy/jit: backend/llgraph metainterp metainterp/test

arigo at codespeak.net arigo at codespeak.net
Mon Dec 7 21:22:54 CET 2009


Author: arigo
Date: Mon Dec  7 21:22:52 2009
New Revision: 69957

Modified:
   pypy/branch/virtual-forcing/pypy/jit/backend/llgraph/llimpl.py
   pypy/branch/virtual-forcing/pypy/jit/metainterp/executor.py
   pypy/branch/virtual-forcing/pypy/jit/metainterp/optimizeopt.py
   pypy/branch/virtual-forcing/pypy/jit/metainterp/pyjitpl.py
   pypy/branch/virtual-forcing/pypy/jit/metainterp/resoperation.py
   pypy/branch/virtual-forcing/pypy/jit/metainterp/test/test_virtualref.py
   pypy/branch/virtual-forcing/pypy/jit/metainterp/virtualizable.py
   pypy/branch/virtual-forcing/pypy/jit/metainterp/virtualref.py
Log:
More tweaks:

* make the code in pyjitpl.py look even more like it is
  for virtualizables;

* implement the more general policy documented in the
  docstring of virtual_ref().


Modified: pypy/branch/virtual-forcing/pypy/jit/backend/llgraph/llimpl.py
==============================================================================
--- pypy/branch/virtual-forcing/pypy/jit/backend/llgraph/llimpl.py	(original)
+++ pypy/branch/virtual-forcing/pypy/jit/backend/llgraph/llimpl.py	Mon Dec  7 21:22:52 2009
@@ -154,6 +154,7 @@
     'call_may_force'  : (('int', 'varargs'), 'intorptr'),
     'guard_not_forced': ((), None),
     'virtual_ref'     : (('ref', 'int'), 'ref'),
+    'virtual_ref_finish':(('ref', 'ref'), None),
     #'getitem'         : (('void', 'ref', 'int'), 'int'),
     #'setitem'         : (('void', 'ref', 'int', 'int'), None),
     #'newlist'         : (('void', 'varargs'), 'ref'),
@@ -817,6 +818,9 @@
     def op_virtual_ref(self, _, virtual, index):
         return virtual
 
+    def op_virtual_ref_finish(self, _, vref, virtual):
+        pass
+
 
 class OOFrame(Frame):
 

Modified: pypy/branch/virtual-forcing/pypy/jit/metainterp/executor.py
==============================================================================
--- pypy/branch/virtual-forcing/pypy/jit/metainterp/executor.py	(original)
+++ pypy/branch/virtual-forcing/pypy/jit/metainterp/executor.py	Mon Dec  7 21:22:52 2009
@@ -6,7 +6,7 @@
 from pypy.rpython.ootypesystem import ootype
 from pypy.rpython.lltypesystem.lloperation import llop
 from pypy.rlib.rarithmetic import ovfcheck, r_uint, intmask
-from pypy.jit.metainterp.history import BoxInt, ConstInt, check_descr
+from pypy.jit.metainterp.history import BoxInt, BoxPtr, ConstInt, check_descr
 from pypy.jit.metainterp.history import INT, REF, ConstFloat
 from pypy.jit.metainterp import resoperation
 from pypy.jit.metainterp.resoperation import rop
@@ -226,6 +226,9 @@
 def do_virtual_ref(cpu, box1, box2):
     raise NotImplementedError
 
+def do_virtual_ref_finish(cpu, box1, box2):
+    raise NotImplementedError
+
 def do_debug_merge_point(cpu, box1):
     from pypy.jit.metainterp.warmspot import get_stats
     loc = box1._get_str()

Modified: pypy/branch/virtual-forcing/pypy/jit/metainterp/optimizeopt.py
==============================================================================
--- pypy/branch/virtual-forcing/pypy/jit/metainterp/optimizeopt.py	(original)
+++ pypy/branch/virtual-forcing/pypy/jit/metainterp/optimizeopt.py	Mon Dec  7 21:22:52 2009
@@ -759,6 +759,27 @@
         vrefvalue.setfield(descr_virtual_token, self.getvalue(tokenbox))
         vrefvalue.setfield(descr_virtualref_index, self.getvalue(indexbox))
 
+    def optimize_VIRTUAL_REF_FINISH(self, op):
+        value = self.getvalue(op.args[1])
+        if not value.is_virtual():   # virtual_ref(non-virtual) gives bad
+            raise compile.GiveUp     # results, so don't bother compiling it
+        #
+        # 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.
+        from pypy.jit.metainterp import virtualref
+        # - set 'forced' to point to the real object
+        op1 = ResOperation(rop.SETFIELD_GC, op.args, None,
+                          descr = virtualref.get_descr_forced(self.cpu))
+        self.optimize_SETFIELD_GC(op1)
+        # - set 'virtual_token' to TOKEN_NONE
+        op1 = ResOperation(rop.SETFIELD_GC, [op.args[0], ConstInt(0)], None,
+                          descr = virtualref.get_descr_virtual_token(self.cpu))
+        self.optimize_SETFIELD_GC(op1)
+
     def optimize_GETFIELD_GC(self, op):
         value = self.getvalue(op.args[0])
         if value.is_virtual():

Modified: pypy/branch/virtual-forcing/pypy/jit/metainterp/pyjitpl.py
==============================================================================
--- pypy/branch/virtual-forcing/pypy/jit/metainterp/pyjitpl.py	(original)
+++ pypy/branch/virtual-forcing/pypy/jit/metainterp/pyjitpl.py	Mon Dec  7 21:22:52 2009
@@ -888,29 +888,37 @@
 
     @arguments("box")
     def opimpl_virtual_ref(self, box):
-        obj = box.getref_base()
-        vref = virtualref.virtual_ref_during_tracing(obj)
-        resbox = history.BoxPtr(vref)
-        cindex = history.ConstInt(len(self.metainterp.virtualref_boxes) // 2)
-        self.metainterp.history.record(rop.VIRTUAL_REF, [box, cindex], resbox)
-        # Note: we allocate a JIT_VIRTUAL_REF here
-        # (in virtual_ref_during_tracing()), in order to detect when
-        # the virtual escapes during tracing already.  We record it as a
-        # VIRTUAL_REF operation, although the backend sees this operation
-        # as a no-op.  The point is that the backend should not really see
-        # it in practice, as optimizeopt.py should either kill it or
-        # replace it with a NEW_WITH_VTABLE followed by SETFIELD_GCs.
-        self.metainterp.virtualref_boxes.append(box)
-        self.metainterp.virtualref_boxes.append(resbox)
+        metainterp = self.metainterp
+        if metainterp.is_blackholing():
+            resbox = box      # good enough when blackholing
+        else:
+            obj = box.getref_base()
+            vref = virtualref.virtual_ref_during_tracing(obj)
+            resbox = history.BoxPtr(vref)
+            cindex = history.ConstInt(len(metainterp.virtualref_boxes) // 2)
+            metainterp.history.record(rop.VIRTUAL_REF, [box, cindex], resbox)
+            # Note: we allocate a JIT_VIRTUAL_REF here
+            # (in virtual_ref_during_tracing()), in order to detect when
+            # the virtual escapes during tracing already.  We record it as a
+            # VIRTUAL_REF operation, although the backend sees this operation
+            # as a no-op.  The point is that the backend should not really see
+            # it in practice, as optimizeopt.py should either kill it or
+            # replace it with a NEW_WITH_VTABLE followed by SETFIELD_GCs.
+        metainterp.virtualref_boxes.append(box)
+        metainterp.virtualref_boxes.append(resbox)
         self.make_result_box(resbox)
 
     @arguments("box")
-    def opimpl_virtual_ref_finish(self, vrefbox):
+    def opimpl_virtual_ref_finish(self, box):
         # virtual_ref_finish() assumes that we have a stack-like, last-in
         # first-out order.
-        lastvrefbox = self.metainterp.virtualref_boxes.pop()
-        assert vrefbox.getref_base() == lastvrefbox.getref_base()
-        self.metainterp.virtualref_boxes.pop()
+        metainterp = self.metainterp
+        vrefbox = metainterp.virtualref_boxes.pop()
+        lastbox = metainterp.virtualref_boxes.pop()
+        assert box.getref_base() == lastbox.getref_base()
+        if not metainterp.is_blackholing():
+            metainterp.history.record(rop.VIRTUAL_REF_FINISH,
+                                      [vrefbox, lastbox], None)
 
     # ------------------------------
 
@@ -1016,12 +1024,11 @@
         effectinfo = descr.get_extra_info()
         if effectinfo is None or effectinfo.forces_virtual_or_virtualizable:
             # residual calls require attention to keep virtualizables in-sync
-            self.metainterp.vable_before_residual_call()
+            self.metainterp.vable_and_vrefs_before_residual_call()
             # xxx do something about code duplication
             resbox = self.metainterp.execute_and_record_varargs(
                 rop.CALL_MAY_FORCE, argboxes, descr=descr)
-            self.metainterp.virtual_after_residual_call()
-            self.metainterp.vable_after_residual_call()
+            self.metainterp.vable_and_vrefs_after_residual_call()
             if resbox is not None:
                 self.make_result_box(resbox)
             self.generate_guard(self.pc, rop.GUARD_NOT_FORCED, None, [])
@@ -1752,9 +1759,17 @@
         virtualizable = vinfo.unwrap_virtualizable_box(virtualizable_box)
         vinfo.clear_vable_token(virtualizable)
 
-    def vable_before_residual_call(self):
+    def vable_and_vrefs_before_residual_call(self):
         if self.is_blackholing():
             return
+        #
+        for i in range(1, len(self.virtualref_boxes), 2):
+            vrefbox = self.virtualref_boxes[i]
+            vref = vrefbox.getref_base()
+            virtualref.tracing_before_residual_call(vref)
+            # the FORCE_TOKEN is already set at runtime in each vrefs when
+            # they are created, by optimizeopt.py.
+        #
         vinfo = self.staticdata.virtualizable_info
         if vinfo is not None:
             virtualizable_box = self.virtualizable_boxes[-1]
@@ -1767,38 +1782,33 @@
                                                   force_token_box],
                                 None, descr=vinfo.vable_token_descr)
 
-    def vable_after_residual_call(self):
+    def vable_and_vrefs_after_residual_call(self):
         if self.is_blackholing():
-            vable_escapes = True
+            escapes = True
         else:
-            vable_escapes = False
+            escapes = False
+            #
+            for i in range(1, len(self.virtualref_boxes), 2):
+                vrefbox = self.virtualref_boxes[i]
+                vref = vrefbox.getref_base()
+                if virtualref.tracing_after_residual_call(vref):
+                    # this vref escaped during CALL_MAY_FORCE.
+                    escapes = True
+            #
             vinfo = self.staticdata.virtualizable_info
             if vinfo is not None:
                 virtualizable_box = self.virtualizable_boxes[-1]
                 virtualizable = vinfo.unwrap_virtualizable_box(virtualizable_box)
                 if vinfo.tracing_after_residual_call(virtualizable):
-                    # We just did the residual call, and it shows that the
-                    # virtualizable escapes.
-                    self.switch_to_blackhole()
-                    vable_escapes = True
-        if vable_escapes:
+                    # the virtualizable escaped during CALL_MAY_FORCE.
+                    escapes = True
+            #
+            if escapes:
+                self.switch_to_blackhole()
+        #
+        if escapes:
             self.load_fields_from_virtualizable()
 
-    def virtual_after_residual_call(self):
-        if self.is_blackholing():
-            return
-        for i in range(1, len(self.virtualref_boxes), 2):
-            vrefbox = self.virtualref_boxes[i]
-            if virtualref.was_forced(vrefbox.getref_base()):
-                break
-        else:
-            return
-        # during tracing, more precisely during the CALL_MAY_FORCE, at least
-        # one of the vrefs was read.  If we continue to trace and make
-        # assembler from there, we will get assembler that probably always
-        # forces a vref.  So we just cancel now.
-        self.switch_to_blackhole()
-
     def handle_exception(self):
         etype = self.cpu.get_exception()
         evalue = self.cpu.get_exc_value()

Modified: pypy/branch/virtual-forcing/pypy/jit/metainterp/resoperation.py
==============================================================================
--- pypy/branch/virtual-forcing/pypy/jit/metainterp/resoperation.py	(original)
+++ pypy/branch/virtual-forcing/pypy/jit/metainterp/resoperation.py	Mon Dec  7 21:22:52 2009
@@ -223,6 +223,7 @@
     'COND_CALL_GC_MALLOC',  # [a, b, if_(a<=b)_result, if_(a>b)_call, args...]
                             #        => result          (for mallocs)
     'DEBUG_MERGE_POINT/1',      # debugging only
+    'VIRTUAL_REF_FINISH/2',
 
     '_CANRAISE_FIRST', # ----- start of can_raise operations -----
     'CALL',

Modified: pypy/branch/virtual-forcing/pypy/jit/metainterp/test/test_virtualref.py
==============================================================================
--- pypy/branch/virtual-forcing/pypy/jit/metainterp/test/test_virtualref.py	(original)
+++ pypy/branch/virtual-forcing/pypy/jit/metainterp/test/test_virtualref.py	Mon Dec  7 21:22:52 2009
@@ -1,5 +1,5 @@
 import py
-from pypy.rpython.lltypesystem import lltype, llmemory
+from pypy.rpython.lltypesystem import lltype, llmemory, lloperation
 from pypy.rlib.jit import JitDriver, dont_look_inside
 from pypy.rlib.jit import virtual_ref, virtual_ref_finish
 from pypy.rlib.objectmodel import compute_unique_id
@@ -7,6 +7,8 @@
 from pypy.jit.metainterp.resoperation import rop
 from pypy.jit.metainterp.virtualref import JIT_VIRTUAL_REF
 
+debug_print = lloperation.llop.debug_print
+
 
 class VRefTests:
 
@@ -18,13 +20,16 @@
         exctx = ExCtx()
         #
         def f():
-            exctx.topframeref = vref = virtual_ref(X())
+            x = X()
+            exctx.topframeref = virtual_ref(x)
             exctx.topframeref = None
-            virtual_ref_finish(vref)
+            virtual_ref_finish(x)
             return 1
         #
         self.interp_operations(f, [])
-        self.check_operations_history(virtual_ref=1)
+        self.check_operations_history(new_with_vtable=1,     # X()
+                                      virtual_ref=1,
+                                      virtual_ref_finish=1)
 
     def test_make_vref_guard(self):
         if not isinstance(self, TestLLtype):
@@ -43,14 +48,16 @@
                 return exctx.topframeref().n
             return n
         def enter(n):
-            exctx.topframeref = virtual_ref(X(n + 10))
+            x = X(n + 10)
+            exctx._frame = x
+            exctx.topframeref = virtual_ref(x)
         def leave():
-            virtual_ref_finish(exctx.topframeref)
             exctx.topframeref = None
+            virtual_ref_finish(exctx._frame)
         def f(n):
             enter(n)
             n = external(n)
-            # ^^^ the point is that X() should be kept alive here
+            # ^^^ the point is that X() and the vref should be kept alive here
             leave()
             return n
         #
@@ -75,6 +82,39 @@
         assert self.metainterp.virtualref_boxes[0].value == bxs1[0].value
         assert self.metainterp.virtualref_boxes[1].value == bxs2[0].value
 
+    def test_make_vref_escape_after_finish(self):
+        jitdriver = JitDriver(greens = [], reds = ['n'])
+        #
+        class X:
+            pass
+        class ExCtx:
+            pass
+        exctx = ExCtx()
+        #
+        @dont_look_inside
+        def g(vref):
+            debug_print(lltype.Void, '-+-+-+-+- external read:', vref().n)
+        #
+        def f(n):
+            while n > 0:
+                jitdriver.can_enter_jit(n=n)
+                jitdriver.jit_merge_point(n=n)
+                x = X()
+                x.n = n
+                exctx.topframeref = vref = virtual_ref(x)
+                # here, 'x' should be virtual. (This is ensured because
+                # we call virtual_ref(x).)
+                exctx.topframeref = None
+                virtual_ref_finish(x)
+                # 'vref' is allowed to escape, and even be forced, even after
+                # the call to finish().
+                g(vref)
+                n -= 1
+            return 1
+        #
+        self.meta_interp(f, [10])
+        self.check_loops(new_with_vtable=2)   # the vref, and later the X
+
     def test_make_vref_and_force(self):
         jitdriver = JitDriver(greens = [], reds = ['total', 'n'])
         #
@@ -97,7 +137,7 @@
                 x.n = n + 123
                 exctx.topframeref = virtual_ref(x)
                 total += force_me() - 100
-                virtual_ref_finish(exctx.topframeref)
+                virtual_ref_finish(x)
                 exctx.topframeref = None
             return total
         #
@@ -127,13 +167,18 @@
                 xy = XY()
                 xy.next1 = XY()
                 xy.next2 = XY()
+                xy.next3 = XY()
                 exctx.topframeref = virtual_ref(xy)
                 n -= externalfn(n)
-                virtual_ref_finish(exctx.topframeref)
                 exctx.topframeref = None
+                xy.next1 = None
+                xy.next2 = None
+                xy.next3 = None
+                virtual_ref_finish(xy)
         #
         self.meta_interp(f, [15])
-        self.check_loops(new_with_vtable=1)     # the vref, not the XYs
+        self.check_loops(new_with_vtable=2)     # the vref, and xy so far,
+                                                # but not xy.next1/2/3
 
     def test_simple_force_always(self):
         myjitdriver = JitDriver(greens = [], reds = ['n'])
@@ -158,7 +203,7 @@
                 xy.n = n
                 exctx.topframeref = virtual_ref(xy)
                 n -= externalfn(n)
-                virtual_ref_finish(exctx.topframeref)
+                virtual_ref_finish(xy)
                 exctx.topframeref = None
         #
         self.meta_interp(f, [15])
@@ -187,14 +232,15 @@
                 xy.n = n
                 exctx.topframeref = virtual_ref(xy)
                 n -= externalfn(n)
-                virtual_ref_finish(exctx.topframeref)
+                virtual_ref_finish(xy)
                 exctx.topframeref = None
             return exctx.m
         #
         res = self.meta_interp(f, [30])
         assert res == 13
+        self.check_loop_count(1)
 
-    def test_bridge_forces(self):
+    def test_blackhole_forces(self):
         myjitdriver = JitDriver(greens = [], reds = ['n'])
         #
         class XY:
@@ -218,12 +264,75 @@
                 if n == 13:
                     externalfn(n)
                 n -= 1
-                virtual_ref_finish(exctx.topframeref)
                 exctx.topframeref = None
+                virtual_ref_finish(xy)
             return exctx.m
         #
         res = self.meta_interp(f, [30])
         assert res == 13
+        self.check_loop_count(1)
+
+    def test_bridge_forces(self):
+        myjitdriver = JitDriver(greens = [], reds = ['n'])
+        #
+        class XY:
+            pass
+        class ExCtx:
+            pass
+        exctx = ExCtx()
+        #
+        @dont_look_inside
+        def externalfn(n):
+            exctx.m = exctx.topframeref().n
+            return 1
+        #
+        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)
+                if n % 6 == 0:
+                    externalfn(n)
+                n -= 1
+                exctx.topframeref = None
+                virtual_ref_finish(xy)
+            return exctx.m
+        #
+        res = self.meta_interp(f, [72])
+        assert res == 6
+        self.check_loop_count(1)     # the bridge should not be compiled
+
+    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):
+            later = None
+            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 = None
+                virtual_ref_finish(xy)
+            return g()
+        #
+        res = self.meta_interp(f, [15])
+        assert res == 1
 
 
 class TestLLtype(VRefTests, LLJitMixin):

Modified: pypy/branch/virtual-forcing/pypy/jit/metainterp/virtualizable.py
==============================================================================
--- pypy/branch/virtual-forcing/pypy/jit/metainterp/virtualizable.py	(original)
+++ pypy/branch/virtual-forcing/pypy/jit/metainterp/virtualizable.py	Mon Dec  7 21:22:52 2009
@@ -11,8 +11,8 @@
 
 
 class VirtualizableInfo:
-    TOKEN_NONE    = 0
-    TOKEN_TRACING = -1
+    TOKEN_NONE            = 0
+    TOKEN_TRACING_RESCALL = -1
 
     def __init__(self, warmrunnerdesc):
         self.warmrunnerdesc = warmrunnerdesc
@@ -186,13 +186,13 @@
 
     def tracing_before_residual_call(self, virtualizable):
         assert not virtualizable.vable_token
-        virtualizable.vable_token = self.TOKEN_TRACING
+        virtualizable.vable_token = self.TOKEN_TRACING_RESCALL
 
     def tracing_after_residual_call(self, virtualizable):
         if virtualizable.vable_token:
             # not modified by the residual call; assert that it is still
-            # set to 'tracing_vable_rti' and clear it.
-            assert virtualizable.vable_token == self.TOKEN_TRACING
+            # set to TOKEN_TRACING_RESCALL and clear it.
+            assert virtualizable.vable_token == self.TOKEN_TRACING_RESCALL
             virtualizable.vable_token = self.TOKEN_NONE
             return False
         else:
@@ -201,7 +201,7 @@
 
     def force_now(self, virtualizable):
         token = virtualizable.vable_token
-        if token == self.TOKEN_TRACING:
+        if token == self.TOKEN_TRACING_RESCALL:
             # The values in the virtualizable are always correct during
             # tracing.  We only need to reset vable_token to TOKEN_NONE
             # as a marker for the tracing, to tell it that this
@@ -228,9 +228,9 @@
 #
 #   2. equal to 0 when tracing is in progress; except:
 #
-#   3. equal to -1 (TOKEN_TRACING) during tracing when we do a residual call,
-#      calling random unknown other parts of the interpreter; it is
-#      reset to 0 as soon as something occurs to the virtualizable.
+#   3. equal to -1 (TOKEN_TRACING_RESCALL) during tracing when we do a
+#      residual call, calling random unknown other parts of the interpreter;
+#      it is reset to 0 as soon as something occurs to the virtualizable.
 #
 #   4. when running the machine code with a virtualizable, it is set
 #      to the address in the CPU stack by the FORCE_TOKEN operation.

Modified: pypy/branch/virtual-forcing/pypy/jit/metainterp/virtualref.py
==============================================================================
--- pypy/branch/virtual-forcing/pypy/jit/metainterp/virtualref.py	(original)
+++ pypy/branch/virtual-forcing/pypy/jit/metainterp/virtualref.py	Mon Dec  7 21:22:52 2009
@@ -43,11 +43,12 @@
 
 # The 'virtual_token' field has the same meaning as the 'vable_token' field
 # of a virtualizable.  It is equal to:
-#   * -1 (TOKEN_TRACING) when tracing;
+#   * 0 (TOKEN_NONE) when tracing, except as described below;
+#   * -1 (TOKEN_TRACING_RESCALL) during tracing when we do a residual call;
 #   * addr in the CPU stack (set by FORCE_TOKEN) when running the assembler;
 #   * 0 (TOKEN_NONE) after the virtual is forced, if it is forced at all.
-TOKEN_NONE    = 0
-TOKEN_TRACING = -1
+TOKEN_NONE            = 0
+TOKEN_TRACING_RESCALL = -1
 
 @specialize.memo()
 def get_jit_virtual_ref_const_class(cpu):
@@ -71,27 +72,40 @@
     vref = lltype.malloc(JIT_VIRTUAL_REF)
     p = lltype.cast_pointer(rclass.OBJECTPTR, vref)
     p.typeptr = jit_virtual_ref_vtable
-    vref.virtual_token = TOKEN_TRACING
+    vref.virtual_token = TOKEN_NONE
     vref.forced = lltype.cast_opaque_ptr(rclass.OBJECTPTR, real_object)
     return lltype.cast_opaque_ptr(llmemory.GCREF, vref)
 
-def was_forced(gcref):
+def tracing_before_residual_call(gcref):
+    vref = lltype.cast_opaque_ptr(lltype.Ptr(JIT_VIRTUAL_REF), gcref)
+    assert not vref.virtual_token
+    vref.virtual_token = TOKEN_TRACING_RESCALL
+
+def tracing_after_residual_call(gcref):
     vref = lltype.cast_opaque_ptr(lltype.Ptr(JIT_VIRTUAL_REF), gcref)
-    return vref.virtual_token != TOKEN_TRACING
+    if vref.virtual_token:
+        # not modified by the residual call; assert that it is still
+        # set to TOKEN_TRACING_RESCALL and clear it.
+        assert vref.virtual_token == TOKEN_TRACING_RESCALL
+        vref.virtual_token = TOKEN_NONE
+        return False
+    else:
+        # marker "modified during residual call" set.
+        return True
 
 def forced_single_vref(gcref, real_object):
     assert real_object
     vref = lltype.cast_opaque_ptr(lltype.Ptr(JIT_VIRTUAL_REF), gcref)
     assert (vref.virtual_token != TOKEN_NONE and
-            vref.virtual_token != TOKEN_TRACING)
-    vref.forced = lltype.cast_opaque_ptr(rclass.OBJECTPTR, real_object)
+            vref.virtual_token != TOKEN_TRACING_RESCALL)
     vref.virtual_token = TOKEN_NONE
+    vref.forced = lltype.cast_opaque_ptr(rclass.OBJECTPTR, real_object)
 
 def continue_tracing(gcref, real_object):
     vref = lltype.cast_opaque_ptr(lltype.Ptr(JIT_VIRTUAL_REF), gcref)
-    assert vref.virtual_token != TOKEN_TRACING
+    assert vref.virtual_token != TOKEN_TRACING_RESCALL
+    vref.virtual_token = TOKEN_NONE
     vref.forced = lltype.cast_opaque_ptr(rclass.OBJECTPTR, real_object)
-    vref.virtual_token = TOKEN_TRACING
 
 # ____________________________________________________________
 
@@ -113,7 +127,7 @@
     vref = lltype.cast_pointer(lltype.Ptr(JIT_VIRTUAL_REF), inst)
     token = vref.virtual_token
     if token != TOKEN_NONE:
-        if token == TOKEN_TRACING:
+        if token == TOKEN_TRACING_RESCALL:
             # The "virtual" is not a virtual at all during tracing.
             # We only need to reset virtual_token to TOKEN_NONE
             # as a marker for the tracing, to tell it that this



More information about the Pypy-commit mailing list