[pypy-svn] r70084 - in pypy/branch/virtual-forcing/pypy: jit/metainterp jit/metainterp/test rlib

arigo at codespeak.net arigo at codespeak.net
Sun Dec 13 17:02:46 CET 2009


Author: arigo
Date: Sun Dec 13 17:02:46 2009
New Revision: 70084

Modified:
   pypy/branch/virtual-forcing/pypy/jit/metainterp/codewriter.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/test/test_virtualref.py
   pypy/branch/virtual-forcing/pypy/rlib/jit.py
Log:
Kill the virtual_ref_check() operation and simplify again the semantics
of virtualrefs.  Now it will no longer cause tracing to abort.  It might
simplify things quite a lot, and give hopefully good results.


Modified: pypy/branch/virtual-forcing/pypy/jit/metainterp/codewriter.py
==============================================================================
--- pypy/branch/virtual-forcing/pypy/jit/metainterp/codewriter.py	(original)
+++ pypy/branch/virtual-forcing/pypy/jit/metainterp/codewriter.py	Sun Dec 13 17:02:46 2009
@@ -1170,9 +1170,6 @@
     def handle_jit_marker__can_enter_jit(self, op):
         self.emit('can_enter_jit')
 
-    def handle_jit_marker__virtual_ref_check(self, op):
-        self.emit('virtual_ref_check')
-
     def serialize_op_direct_call(self, op):
         kind = self.codewriter.guess_call_kind(op)
         return getattr(self, 'handle_%s_call' % kind)(op)

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	Sun Dec 13 17:02:46 2009
@@ -737,9 +737,6 @@
         self._optimize_oois_ooisnot(op, False)
 
     def optimize_VIRTUAL_REF(self, op):
-        value = self.getvalue(op.args[0])
-        if not value.is_virtual():   # virtual_ref(non-virtual) gives bad
-            raise compile.GiveUp     # results, so don't bother compiling it
         indexbox = op.args[1]
         #
         # get some constants (these calls are all 'memo')
@@ -760,10 +757,6 @@
         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) means it
-            raise compile.GiveUp     # escaped already, which is bad
-        #
         # 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
@@ -780,6 +773,12 @@
         op1 = ResOperation(rop.SETFIELD_GC, args, None,
                       descr = virtualref.get_descr_virtual_token(self.cpu))
         self.optimize_SETFIELD_GC(op1)
+        # Note that in some cases the virtual in op.args[1] has been forced
+        # already.  This is fine.  In that case, and *if* a residual
+        # CALL_MAY_FORCE suddenly turns out to access it, then it will
+        # trigger a ResumeGuardForcedDescr.handle_async_forcing() which
+        # will work too (but just be a little pointless, as the structure
+        # was already forced).
 
     def optimize_GETFIELD_GC(self, op):
         value = self.getvalue(op.args[0])

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	Sun Dec 13 17:02:46 2009
@@ -508,13 +508,6 @@
     @arguments("box", "descr", "box")
     def opimpl_setfield_gc(self, box, fielddesc, valuebox):
         self.execute_with_descr(rop.SETFIELD_GC, fielddesc, box, valuebox)
-        # <hack> XXX
-        metainterp = self.metainterp
-        if (len(metainterp.virtualref_boxes) >= 2 and
-            metainterp.virtualref_boxes[-2] == valuebox and
-            virtualref.is_virtual_ref(metainterp.virtualref_boxes[-1].getref_base())):
-            metainterp.stop_tracking_virtualref(-2)
-        # </hack>
 
     @arguments("box", "descr")
     def opimpl_getfield_raw(self, box, fielddesc):
@@ -843,7 +836,6 @@
                 except GiveUp:
                     self.metainterp.staticdata.profiler.count(ABORT_BRIDGE)
                     self.metainterp.switch_to_blackhole()
-            self.metainterp.virtual_ref_check()
         if self.metainterp.is_blackholing():
             self.blackhole_reached_merge_point(self.env)
         return True
@@ -907,11 +899,6 @@
         #    in the vref), then we replace the vref in the list with
         #    ConstPtr(NULL).
         #
-        #  * at the next virtual_ref_check() or jit_merge_point(), any such
-        #    NULL causes tracing to abort.  So escaping is fine if there
-        #    is no virtual_ref_check()/jit_merge_point() before the final
-        #    call to virtual_ref_finish().
-        #
         metainterp = self.metainterp
         if metainterp.is_blackholing():
             resbox = box      # good enough when blackholing
@@ -946,10 +933,6 @@
                 metainterp.history.record(rop.VIRTUAL_REF_FINISH,
                                           [vrefbox, lastbox], None)
 
-    @arguments()
-    def opimpl_virtual_ref_check(self):
-        self.metainterp.virtual_ref_check()
-
     # ------------------------------
 
     def setup_call(self, argboxes):
@@ -1058,7 +1041,7 @@
             # xxx do something about code duplication
             resbox = self.metainterp.execute_and_record_varargs(
                 rop.CALL_MAY_FORCE, argboxes, descr=descr)
-            self.metainterp.vable_and_vrefs_after_residual_call(argboxes)
+            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, [])
@@ -1813,7 +1796,7 @@
                                                   force_token_box],
                                 None, descr=vinfo.vable_token_descr)
 
-    def vable_and_vrefs_after_residual_call(self, argboxes):
+    def vable_and_vrefs_after_residual_call(self):
         if self.is_blackholing():
             escapes = True
         else:
@@ -1823,8 +1806,7 @@
                 virtualbox = self.virtualref_boxes[i]
                 vrefbox = self.virtualref_boxes[i+1]
                 vref = vrefbox.getref_base()
-                if (virtualref.tracing_after_residual_call(vref) or
-                    virtualbox in argboxes):    # <-- XXX hack
+                if virtualref.tracing_after_residual_call(vref):
                     # this vref was really a virtual_ref, but it escaped
                     # during this CALL_MAY_FORCE.  Mark this fact by
                     # generating a VIRTUAL_REF_FINISH on it and replacing
@@ -1848,25 +1830,15 @@
     def stop_tracking_virtualref(self, i):
         virtualbox = self.virtualref_boxes[i]
         vrefbox = self.virtualref_boxes[i+1]
-        # record VIRTUAL_REF_FINISH just before the current op
-        current_op = self.history.operations.pop()
+        # record VIRTUAL_REF_FINISH just before the current CALL_MAY_FORCE
+        call_may_force_op = self.history.operations.pop()
+        assert call_may_force_op.opnum == rop.CALL_MAY_FORCE
         self.history.record(rop.VIRTUAL_REF_FINISH,
                             [vrefbox, virtualbox], None)
-        self.history.operations.append(current_op)
+        self.history.operations.append(call_may_force_op)
         # mark by replacing it with ConstPtr(NULL)
         self.virtualref_boxes[i+1] = self.cpu.ts.CONST_NULL
 
-    def virtual_ref_check(self):
-        if self.is_blackholing():
-            return
-        # abort tracing if we reach this point with an escaped virtual
-        for i in range(1, len(self.virtualref_boxes), 2):
-            vrefbox = self.virtualref_boxes[i]
-            vref = vrefbox.getref_base()
-            if not virtualref.is_virtual_ref(vref):
-                self.switch_to_blackhole()
-                break
-
     def handle_exception(self):
         etype = self.cpu.get_exception()
         evalue = self.cpu.get_exc_value()

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	Sun Dec 13 17:02:46 2009
@@ -1,7 +1,7 @@
 import py
 from pypy.rpython.lltypesystem import lltype, llmemory, lloperation
 from pypy.rlib.jit import JitDriver, dont_look_inside, vref_None
-from pypy.rlib.jit import virtual_ref, virtual_ref_check, virtual_ref_finish
+from pypy.rlib.jit import virtual_ref, virtual_ref_finish
 from pypy.rlib.objectmodel import compute_unique_id
 from pypy.jit.metainterp.test.test_basic import LLJitMixin, OOJitMixin
 from pypy.jit.metainterp.resoperation import rop
@@ -22,7 +22,6 @@
         def f():
             x = X()
             exctx.topframeref = virtual_ref(x)
-            virtual_ref_check()
             exctx.topframeref = vref_None
             virtual_ref_finish(x)
             return 1
@@ -54,7 +53,6 @@
             exctx.topframeref = virtual_ref(x)
         def leave():
             exctx.topframeref = vref_None
-            virtual_ref_check()
             virtual_ref_finish(exctx._frame)
         def f(n):
             enter(n)
@@ -107,10 +105,9 @@
                 # here, 'x' should be virtual. (This is ensured because
                 # we call virtual_ref(x).)
                 exctx.topframeref = vref_None
-                virtual_ref_check()
                 virtual_ref_finish(x)
-                # 'x' is allowed to escape, and even be forced, even after
-                # the call to finish().
+                # 'x' and 'vref' can randomly escape after the call to
+                # finish().
                 g(vref)
                 n -= 1
             return 1
@@ -119,104 +116,10 @@
         self.check_loops(new_with_vtable=2)   # the vref, and later the X
         self.check_aborted_count(0)
 
-    def test_make_vref_and_force_nocheck_1(self):
-        jitdriver = JitDriver(greens = [], reds = ['total', 'n'])
-        #
-        class X:
-            pass
-        class ExCtx:
-            pass
-        exctx = ExCtx()
-        #
-        @dont_look_inside
-        def force_me():
-            return exctx.topframeref().n
-        #
-        def f(n):
-            total = 0
-            while total < 300:
-                jitdriver.can_enter_jit(total=total, n=n)
-                jitdriver.jit_merge_point(total=total, n=n)
-                x = X()
-                x.n = n + 123
-                exctx.topframeref = virtual_ref(x)
-                # --- no virtual_ref_check() here ---
-                total += force_me() - 100
-                virtual_ref_finish(x)
-                exctx.topframeref = vref_None
-            return total
-        #
-        res = self.meta_interp(f, [-4])
-        assert res == 16 * 19
-        self.check_aborted_count(0)
-
-    def test_make_vref_and_force_nocheck_2(self):
-        jitdriver = JitDriver(greens = [], reds = ['total', 'n'])
-        #
-        class X:
-            pass
-        class ExCtx:
-            pass
-        exctx = ExCtx()
-        #
-        @dont_look_inside
-        def force_me():
-            return exctx.topframeref().n
-        #
-        def f(n):
-            total = 0
-            while total < 300:
-                jitdriver.can_enter_jit(total=total, n=n)
-                jitdriver.jit_merge_point(total=total, n=n)
-                x = X()
-                x.n = n + 123
-                exctx.topframeref = virtual_ref(x)
-                virtual_ref_check()
-                total += force_me() - 100
-                # --- but no virtual_ref_check() there ---
-                virtual_ref_finish(x)
-                exctx.topframeref = vref_None
-            return total
-        #
-        res = self.meta_interp(f, [-4])
-        assert res == 16 * 19
-        self.check_aborted_count(0)
-
-    def test_make_vref_and_force_check(self):
-        jitdriver = JitDriver(greens = [], reds = ['total', 'n'])
-        #
-        class X:
-            pass
-        class ExCtx:
-            pass
-        exctx = ExCtx()
-        #
-        @dont_look_inside
-        def force_me():
-            return exctx.topframeref().n
-        #
-        def f(n):
-            total = 0
-            while total < 300:
-                jitdriver.can_enter_jit(total=total, n=n)
-                jitdriver.jit_merge_point(total=total, n=n)
-                x = X()
-                x.n = n + 123
-                exctx.topframeref = virtual_ref(x)
-                total += force_me() - 100
-                virtual_ref_check()
-                virtual_ref_finish(x)
-                exctx.topframeref = vref_None
-            return total
-        #
-        res = self.meta_interp(f, [-4])
-        assert res == 16 * 19
-        self.check_loops({})      # because we aborted tracing
-        self.check_aborted_count_at_least(1)
-
     def test_simple_no_access(self):
         myjitdriver = JitDriver(greens = [], reds = ['n'])
         #
+        A = lltype.GcArray(lltype.Signed)
         class XY:
             pass
         class ExCtx:
@@ -234,26 +137,26 @@
                 myjitdriver.can_enter_jit(n=n)
                 myjitdriver.jit_merge_point(n=n)
                 xy = XY()
-                xy.next1 = XY()
-                xy.next2 = XY()
-                xy.next3 = XY()
+                xy.next1 = lltype.malloc(A, 0)
+                xy.next2 = lltype.malloc(A, 0)
+                xy.next3 = lltype.malloc(A, 0)
                 exctx.topframeref = virtual_ref(xy)
                 n -= externalfn(n)
                 exctx.topframeref = vref_None
-                xy.next1 = None
-                xy.next2 = None
-                xy.next3 = None
-                virtual_ref_check()
+                xy.next1 = lltype.nullptr(A)
+                xy.next2 = lltype.nullptr(A)
+                xy.next3 = lltype.nullptr(A)
                 virtual_ref_finish(xy)
         #
         self.meta_interp(f, [15])
-        self.check_loops(new_with_vtable=2)     # the vref, and xy so far,
-                                                # but not xy.next1/2/3
+        self.check_loops(new_with_vtable=2,     # the vref, and xy so far,
+                         new_array=0)           # but not xy.next1/2/3
         self.check_aborted_count(0)
 
     def test_simple_force_always(self):
         myjitdriver = JitDriver(greens = [], reds = ['n'])
         #
+        A = lltype.GcArray(lltype.Signed)
         class XY:
             pass
         class ExCtx:
@@ -271,20 +174,27 @@
                 myjitdriver.can_enter_jit(n=n)
                 myjitdriver.jit_merge_point(n=n)
                 xy = XY()
+                xy.next1 = lltype.malloc(A, 0)
+                xy.next2 = lltype.malloc(A, 0)
+                xy.next3 = lltype.malloc(A, 0)
                 xy.n = n
                 exctx.topframeref = virtual_ref(xy)
                 n -= externalfn(n)
-                virtual_ref_check()
+                xy.next1 = lltype.nullptr(A)
+                xy.next2 = lltype.nullptr(A)
+                xy.next3 = lltype.nullptr(A)
                 virtual_ref_finish(xy)
                 exctx.topframeref = vref_None
         #
         self.meta_interp(f, [15])
-        self.check_loops({})     # because we aborted tracing
-        self.check_aborted_count_at_least(1)
+        self.check_loops(new_with_vtable=2,   # XY(), the vref
+                         new_array=3)         # next1/2/3
+        self.check_aborted_count(0)
 
     def test_simple_force_sometimes(self):
         myjitdriver = JitDriver(greens = [], reds = ['n'])
         #
+        A = lltype.GcArray(lltype.Signed)
         class XY:
             pass
         class ExCtx:
@@ -302,22 +212,30 @@
                 myjitdriver.can_enter_jit(n=n)
                 myjitdriver.jit_merge_point(n=n)
                 xy = XY()
+                xy.next1 = lltype.malloc(A, 0)
+                xy.next2 = lltype.malloc(A, 0)
+                xy.next3 = lltype.malloc(A, 0)
                 xy.n = n
                 exctx.topframeref = virtual_ref(xy)
                 n -= externalfn(n)
-                virtual_ref_check()
+                xy.next1 = lltype.nullptr(A)
+                xy.next2 = lltype.nullptr(A)
+                xy.next3 = lltype.nullptr(A)
                 virtual_ref_finish(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_loop_count(1)
         self.check_aborted_count(0)
 
     def test_blackhole_forces(self):
         myjitdriver = JitDriver(greens = [], reds = ['n'])
         #
+        A = lltype.GcArray(lltype.Signed)
         class XY:
             pass
         class ExCtx:
@@ -334,24 +252,32 @@
                 myjitdriver.can_enter_jit(n=n)
                 myjitdriver.jit_merge_point(n=n)
                 xy = XY()
+                xy.next1 = lltype.malloc(A, 0)
+                xy.next2 = lltype.malloc(A, 0)
+                xy.next3 = lltype.malloc(A, 0)
                 xy.n = n
                 exctx.topframeref = virtual_ref(xy)
                 if n == 13:
                     externalfn(n)
                 n -= 1
                 exctx.topframeref = vref_None
-                virtual_ref_check()
+                xy.next1 = lltype.nullptr(A)
+                xy.next2 = lltype.nullptr(A)
+                xy.next3 = lltype.nullptr(A)
                 virtual_ref_finish(xy)
             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_loop_count(1)
         self.check_aborted_count(0)
 
     def test_bridge_forces(self):
         myjitdriver = JitDriver(greens = [], reds = ['n'])
         #
+        A = lltype.GcArray(lltype.Signed)
         class XY:
             pass
         class ExCtx:
@@ -368,20 +294,34 @@
                 myjitdriver.can_enter_jit(n=n)
                 myjitdriver.jit_merge_point(n=n)
                 xy = XY()
+                xy.next1 = lltype.malloc(A, 0)
+                xy.next2 = lltype.malloc(A, 0)
+                xy.next3 = lltype.malloc(A, 0)
+                xy.next4 = lltype.malloc(A, 0)
+                xy.next5 = lltype.malloc(A, 0)
                 xy.n = n
                 exctx.topframeref = virtual_ref(xy)
                 if n % 6 == 0:
+                    xy.next1 = lltype.nullptr(A)
+                    xy.next2 = lltype.nullptr(A)
+                    xy.next3 = lltype.nullptr(A)
                     externalfn(n)
                 n -= 1
                 exctx.topframeref = vref_None
-                virtual_ref_check()
+                xy.next1 = lltype.nullptr(A)
+                xy.next2 = lltype.nullptr(A)
+                xy.next3 = lltype.nullptr(A)
+                xy.next4 = lltype.nullptr(A)
+                xy.next5 = lltype.nullptr(A)
                 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
-        self.check_aborted_count_at_least(1)
+        self.check_loop_count(2)     # the loop and the bridge
+        self.check_loops(new_with_vtable=3,  # loop: vref, xy; bridge: xy
+                         new_array=2)        # bridge: next4, next5
+        self.check_aborted_count(0)
 
     def test_access_vref_later(self):
         myjitdriver = JitDriver(greens = [], reds = ['n'])
@@ -406,7 +346,6 @@
                 exctx.later = exctx.topframeref
                 n -= 1
                 exctx.topframeref = vref_None
-                virtual_ref_check()
                 virtual_ref_finish(xy)
             return g()
         #
@@ -417,6 +356,7 @@
     def test_jit_force_virtual_seen(self):
         myjitdriver = JitDriver(greens = [], reds = ['n'])
         #
+        A = lltype.GcArray(lltype.Signed)
         class XY:
             pass
         class ExCtx:
@@ -430,16 +370,18 @@
                 xy = XY()
                 xy.n = n
                 exctx.topframeref = 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_check()
                 virtual_ref_finish(xy)
             return 1
         #
         res = self.meta_interp(f, [15])
         assert res == 1
-        self.check_loops({})      # because we aborted tracing
-        self.check_aborted_count_at_least(1)
+        self.check_loops(new_with_vtable=2,     # vref, xy
+                         new_array=1)           # next1
+        self.check_aborted_count(0)
 
     def test_recursive_call_1(self):
         myjitdriver = JitDriver(greens = [], reds = ['n', 'frame', 'rec'])

Modified: pypy/branch/virtual-forcing/pypy/rlib/jit.py
==============================================================================
--- pypy/branch/virtual-forcing/pypy/rlib/jit.py	(original)
+++ pypy/branch/virtual-forcing/pypy/rlib/jit.py	Sun Dec 13 17:02:46 2009
@@ -103,27 +103,17 @@
 # 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 (the
-    JIT will abort if it is not), at least between the calls to
-    virtual_ref and virtual_ref_finish.  The point is that the 'vref'
-    returned by virtual_ref may escape early.  If at runtime it is
-    dereferenced (by the call syntax 'vref()') before the
-    virtual_ref_finish, then we get out of the assembler.  If it is not
-    dereferenced at all, or only after the virtual_ref_finish, then
-    nothing special occurs.  Note that the checks for 'being virtual'
-    only occurs when virtual_ref_check() is called (mostly for testing),
-    or when jit_merge_point is called by JITted code in a recursive call.
-    """
+    is that the object 'x' is supposed to be JITted as a virtual between
+    the calls to virtual_ref and virtual_ref_finish, but the 'vref'
+    object can escape at any point in time.  If at runtime it is
+    dereferenced (by the call syntax 'vref()'), it returns 'x', which is
+    then forced."""
     return DirectJitVRef(x)
 virtual_ref.oopspec = 'virtual_ref(x)'
 
-def virtual_ref_check():
-    from pypy.rpython.lltypesystem import lltype, lloperation
-    lloperation.llop.jit_marker(lltype.Void,
-                                lloperation.void('virtual_ref_check'))
-
 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."""



More information about the Pypy-commit mailing list