Author: Armin Rigo <[email protected]>
Branch: 
Changeset: r48086:d48eaa036796
Date: 2011-10-16 16:50 +0200
http://bitbucket.org/pypy/pypy/changeset/d48eaa036796/

Log:    Attempt to fix sys._current_frames() by returning fake frame
        objects.

diff --git a/pypy/jit/codewriter/jtransform.py 
b/pypy/jit/codewriter/jtransform.py
--- a/pypy/jit/codewriter/jtransform.py
+++ b/pypy/jit/codewriter/jtransform.py
@@ -1542,6 +1542,10 @@
     def rewrite_op_jit_force_virtual(self, op):
         return self._do_builtin_call(op)
 
+    def rewrite_op_jit_is_virtual(self, op):
+        raise Exception, (
+            "'vref.virtual' should not be used from jit-visible code")
+
     def rewrite_op_jit_force_virtualizable(self, op):
         # this one is for virtualizables
         vinfo = self.get_vinfo(op.args[0])
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
@@ -3,6 +3,7 @@
 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, InvalidVirtualRef
+from pypy.rlib.jit import non_virtual_ref
 from pypy.rlib.objectmodel import compute_unique_id
 from pypy.jit.metainterp.test.support import LLJitMixin, OOJitMixin, 
_get_jitcodes
 from pypy.jit.metainterp.resoperation import rop
@@ -595,6 +596,65 @@
         res = self.meta_interp(fn, [10])
         assert res == 6
 
+    def test_is_virtual(self):
+        myjitdriver = JitDriver(greens=[], reds=['n', 'res1'])
+        class X:
+            pass
+        @dont_look_inside
+        def residual(vref):
+            return vref.virtual
+        #
+        def f(n):
+            res1 = -42
+            while n > 0:
+                myjitdriver.jit_merge_point(n=n, res1=res1)
+                x = X()
+                vref = virtual_ref(x)
+                res1 = residual(vref)
+                virtual_ref_finish(vref, x)
+                n -= 1
+            return res1
+        #
+        res = self.meta_interp(f, [10])
+        assert res == 1
+
+    def test_is_not_virtual_none(self):
+        myjitdriver = JitDriver(greens=[], reds=['n', 'res1'])
+        @dont_look_inside
+        def residual(vref):
+            return vref.virtual
+        #
+        def f(n):
+            res1 = -42
+            while n > 0:
+                myjitdriver.jit_merge_point(n=n, res1=res1)
+                res1 = residual(vref_None)
+                n -= 1
+            return res1
+        #
+        res = self.meta_interp(f, [10])
+        assert res == 0
+
+    def test_is_not_virtual_non_none(self):
+        myjitdriver = JitDriver(greens=[], reds=['n', 'res1'])
+        class X:
+            pass
+        @dont_look_inside
+        def residual(vref):
+            return vref.virtual
+        #
+        def f(n):
+            res1 = -42
+            while n > 0:
+                myjitdriver.jit_merge_point(n=n, res1=res1)
+                x = X()
+                res1 = residual(non_virtual_ref(x))
+                n -= 1
+            return res1
+        #
+        res = self.meta_interp(f, [10])
+        assert res == 0
+
 
 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
@@ -39,6 +39,7 @@
     def replace_force_virtual_with_call(self, graphs):
         # similar to rvirtualizable2.replace_force_virtualizable_with_call().
         c_force_virtual_ptr = None
+        c_is_virtual_ptr = None
         force_virtual_count = 0
         for graph in graphs:
             for block in graph.iterblocks():
@@ -52,6 +53,13 @@
                         op.opname = 'direct_call'
                         op.args = [c_force_virtual_ptr, op.args[0]]
                         force_virtual_count += 1
+                    #
+                    if op.opname == 'jit_is_virtual':
+                        if c_is_virtual_ptr is None:
+                            c_is_virtual_ptr = self.get_is_virtual_fnptr()
+                        #
+                        op.opname = 'direct_call'
+                        op.args = [c_is_virtual_ptr, op.args[0]]
         #
         if c_force_virtual_ptr is not None:
             log("replaced %d 'jit_force_virtual' with %r" % 
(force_virtual_count,
@@ -129,6 +137,17 @@
             force_virtual_if_necessary)
         return inputconst(lltype.typeOf(funcptr), funcptr)
 
+    def get_is_virtual_fnptr(self):
+        #
+        def is_virtual(inst):
+            if not inst:
+                return False
+            return inst.typeptr == self.jit_virtual_ref_vtable
+        #
+        FUNC = lltype.FuncType([rclass.OBJECTPTR], lltype.Bool)
+        funcptr = self.warmrunnerdesc.helper_func(lltype.Ptr(FUNC), is_virtual)
+        return inputconst(lltype.typeOf(funcptr), funcptr)
+
     def force_virtual(self, inst):
         vref = lltype.cast_pointer(lltype.Ptr(self.JIT_VIRTUAL_REF), inst)
         token = vref.virtual_token
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
@@ -47,7 +47,7 @@
         'pypy_initial_path'     : 'state.pypy_initial_path',
 
         '_getframe'             : 'vm._getframe', 
-        '_current_frames'       : 'vm._current_frames', 
+        '_current_frames'       : 'currentframes._current_frames', 
         'setrecursionlimit'     : 'vm.setrecursionlimit', 
         'getrecursionlimit'     : 'vm.getrecursionlimit', 
         'setcheckinterval'      : 'vm.setcheckinterval', 
diff --git a/pypy/module/sys/currentframes.py b/pypy/module/sys/currentframes.py
new file mode 100644
--- /dev/null
+++ b/pypy/module/sys/currentframes.py
@@ -0,0 +1,78 @@
+"""
+Implementation of the 'sys._current_frames()' routine.
+"""
+from pypy.interpreter import gateway
+
+app = gateway.applevel('''
+"NOT_RPYTHON"
+import __builtin__
+
+class fake_code(object):
+    co_name = "?"
+    co_filename = "?"
+    co_firstlineno = 0
+
+class fake_frame(object):
+    f_back = None
+    f_builtins = __builtin__.__dict__
+    f_code = fake_code()
+    f_exc_traceback = None
+    f_exc_type = None
+    f_exc_value = None
+    f_globals = {}
+    f_lasti = -1
+    f_lineno = 0
+    f_locals = {}
+    f_restricted = False
+    f_trace = None
+
+    def __init__(self, f):
+        if f is not None:
+            for name in ["f_builtins", "f_code", "f_globals", "f_lasti",
+                         "f_lineno"]:
+                setattr(self, name, getattr(f, name))
+''')
+
+def _current_frames(space):
+    """_current_frames() -> dictionary
+
+    Return a dictionary mapping each current thread T's thread id to T's
+    current stack "frame".  Functions in the traceback module can build the
+    call stack given such a frame.
+
+    Note that in PyPy this returns fake frame objects, to avoid a runtime
+    penalty everywhere with the JIT.  (So far these fake frames can be
+    completely uninformative depending on the JIT state; we could return
+    more with more efforts.)
+
+    This function should be used for specialized purposes only."""
+    w_result = space.newdict()
+    w_fake_frame = app.wget(space, "fake_frame")
+    w_fake_code  = app.wget(space, "fake_code")
+    ecs = space.threadlocals.getallvalues()
+    for thread_ident, ec in ecs.items():
+        vref = ec.topframeref
+        frames = []
+        while not vref.virtual:
+            f = vref()
+            if f is None:
+                break
+            frames.append(f)
+            vref = f.f_backref
+        else:
+            frames.append(None)
+        #
+        w_topframe = space.wrap(None)
+        w_prevframe = None
+        for f in frames:
+            w_nextframe = space.call_function(w_fake_frame, space.wrap(f))
+            if w_prevframe is None:
+                w_topframe = w_nextframe
+            else:
+                space.setattr(w_prevframe, space.wrap('f_back'), w_nextframe)
+            w_prevframe = w_nextframe
+        #
+        space.setitem(w_result,
+                      space.wrap(thread_ident),
+                      w_topframe)
+    return w_result
diff --git a/pypy/module/sys/test/test_sysmodule.py 
b/pypy/module/sys/test/test_sysmodule.py
--- a/pypy/module/sys/test/test_sysmodule.py
+++ b/pypy/module/sys/test/test_sysmodule.py
@@ -556,7 +556,7 @@
             return sys._current_frames()
         frames = f()
         assert frames.keys() == [0]
-        assert frames[0].f_code.co_name == 'f'
+        assert frames[0].f_code.co_name in ('f', '?')
 
 class AppTestCurrentFramesWithThread(AppTestCurrentFrames):
     def setup_class(cls):
@@ -585,8 +585,8 @@
         frames = f()
         lock1.release()
         thisframe = frames.pop(thread_id)
-        assert thisframe.f_code.co_name == 'f'
+        assert thisframe.f_code.co_name in ('f', '?')
 
         assert len(frames) == 1
         _, other_frame = frames.popitem()
-        assert other_frame.f_code.co_name == 'other_thread'
+        assert other_frame.f_code.co_name in ('other_thread', '?')
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
@@ -45,25 +45,6 @@
     f.mark_as_escaped()
     return space.wrap(f)
 
-def _current_frames(space):
-    """_current_frames() -> dictionary
-
-    Return a dictionary mapping each current thread T's thread id to T's
-    current stack frame.
-
-    This function should be used for specialized purposes only."""
-    raise OperationError(space.w_NotImplementedError,
-        space.wrap("XXX sys._current_frames() incompatible with the JIT"))
-    w_result = space.newdict()
-    ecs = space.threadlocals.getallvalues()
-    for thread_ident, ec in ecs.items():
-        f = ec.gettopframe_nohidden()
-        f.mark_as_escaped()
-        space.setitem(w_result,
-                      space.wrap(thread_ident),
-                      space.wrap(f))
-    return w_result
-
 def setrecursionlimit(space, w_new_limit):
     """setrecursionlimit() sets the maximum number of nested calls that
 can occur before a RuntimeError is raised.  On PyPy the limit is
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
@@ -25,6 +25,10 @@
     def simple_call(self):
         return self.s_instance
 
+    def getattr(self, s_attr):
+        assert s_attr.const == 'virtual'
+        return annmodel.s_Bool
+
     def rtyper_makerepr(self, rtyper):
         if rtyper.type_system.name == 'lltypesystem':
             return vrefrepr
@@ -61,6 +65,13 @@
                              " prebuilt virtual_ref")
         return lltype.nullptr(OBJECTPTR.TO)
 
+    def rtype_getattr(self, hop):
+        s_attr = hop.args_s[1]
+        assert s_attr.const == 'virtual'
+        v = hop.inputarg(self, arg=0)
+        hop.exception_cannot_occur()
+        return hop.genop('jit_is_virtual', [v], resulttype = lltype.Bool)
+
 from pypy.rpython.ootypesystem.rclass import OBJECT
 
 class OOVRefRepr(VRefRepr):
diff --git a/pypy/rlib/jit.py b/pypy/rlib/jit.py
--- a/pypy/rlib/jit.py
+++ b/pypy/rlib/jit.py
@@ -317,6 +317,12 @@
             raise InvalidVirtualRef
         return self._x
 
+    @property
+    def virtual(self):
+        """A property that is True if the vref contains a virtual that would
+        be forced by the '()' operator."""
+        return self._state == 'non-forced'
+
     def _finish(self):
         if self._state == 'non-forced':
             self._state = 'invalid'
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
@@ -27,10 +27,13 @@
     x1 = X()
     vref = virtual_ref(x1)
     assert vref._state == 'non-forced'
+    assert vref.virtual is True
     assert vref() is x1
     assert vref._state == 'forced'
+    assert vref.virtual is False
     virtual_ref_finish(vref, x1)
     assert vref._state == 'forced'
+    assert vref.virtual is False
     assert vref() is x1
 
 def test_direct_invalid():
@@ -135,6 +138,13 @@
         x = self.interpret(f, [])
         assert x == 42
 
+    def test_rtype_virtualattr(self):
+        def f():
+            vref = virtual_ref(X())
+            return vref.virtual
+        x = self.interpret(f, [])
+        assert x is False
+
 
 class TestLLtype(BaseTestVRef, LLRtypeMixin):
     OBJECTTYPE = OBJECTPTR
diff --git a/pypy/rpython/lltypesystem/lloperation.py 
b/pypy/rpython/lltypesystem/lloperation.py
--- a/pypy/rpython/lltypesystem/lloperation.py
+++ b/pypy/rpython/lltypesystem/lloperation.py
@@ -428,6 +428,7 @@
     'jit_marker':           LLOp(),
     'jit_force_virtualizable':LLOp(canrun=True),
     'jit_force_virtual':    LLOp(canrun=True),
+    'jit_is_virtual':       LLOp(canrun=True),
     'jit_force_quasi_immutable': LLOp(canrun=True),
     'get_exception_addr':   LLOp(),
     'get_exc_value_addr':   LLOp(),
diff --git a/pypy/rpython/lltypesystem/opimpl.py 
b/pypy/rpython/lltypesystem/opimpl.py
--- a/pypy/rpython/lltypesystem/opimpl.py
+++ b/pypy/rpython/lltypesystem/opimpl.py
@@ -538,6 +538,9 @@
 def op_jit_force_virtual(x):
     return x
 
+def op_jit_is_virtual(x):
+    return False
+
 def op_jit_force_quasi_immutable(*args):
     pass
 
diff --git a/pypy/translator/c/funcgen.py b/pypy/translator/c/funcgen.py
--- a/pypy/translator/c/funcgen.py
+++ b/pypy/translator/c/funcgen.py
@@ -833,7 +833,7 @@
         return 'INSTRUMENT_COUNT(%s);' % counter_label
             
     def OP_IS_EARLY_CONSTANT(self, op):
-        return self.expr(op.result)  + ' = 0;' # Allways false
+        return '%s = 0; /* IS_EARLY_CONSTANT */' % (self.expr(op.result),)
 
     def OP_JIT_MARKER(self, op):
         return '/* JIT_MARKER %s */' % op
@@ -845,6 +845,9 @@
         return '%s = %s; /* JIT_FORCE_VIRTUAL */' % (self.expr(op.result),
                                                      self.expr(op.args[0]))
 
+    def OP_JIT_IS_VIRTUAL(self, op):
+        return '%s = 0; /* JIT_IS_VIRTUAL */' % (self.expr(op.result),)
+
     def OP_JIT_FORCE_QUASI_IMMUTABLE(self, op):
         return '/* JIT_FORCE_QUASI_IMMUTABLE %s */' % op
 
_______________________________________________
pypy-commit mailing list
[email protected]
http://mail.python.org/mailman/listinfo/pypy-commit

Reply via email to