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