[pypy-svn] r67466 - in pypy/branch/spine-of-frames/pypy/interpreter: . test
cfbolz at codespeak.net
cfbolz at codespeak.net
Fri Sep 4 11:41:23 CEST 2009
Author: cfbolz
Date: Fri Sep 4 11:41:23 2009
New Revision: 67466
Modified:
pypy/branch/spine-of-frames/pypy/interpreter/executioncontext.py
pypy/branch/spine-of-frames/pypy/interpreter/pyframe.py
pypy/branch/spine-of-frames/pypy/interpreter/test/test_executioncontext.py
Log:
(pedronis, cfbolz, arigo watching): write really explicit tests about the frame handling. Also refactor
things so that all the obscure stuff is living in one place, in some methods on the execution context.
Modified: pypy/branch/spine-of-frames/pypy/interpreter/executioncontext.py
==============================================================================
--- pypy/branch/spine-of-frames/pypy/interpreter/executioncontext.py (original)
+++ pypy/branch/spine-of-frames/pypy/interpreter/executioncontext.py Fri Sep 4 11:41:23 2009
@@ -17,10 +17,7 @@
def __init__(self, space):
self.space = space
- # 'some_frame' points to any frame from this thread's frame stack
- # (although in general it should point to the top one).
- self.some_frame = None
- self.framestackdepth = 0
+ self._init_frame_chain()
# tracing: space.frame_trace_action.fire() must be called to ensure
# that tracing occurs whenever self.w_tracefunc or self.is_tracing
# is modified.
@@ -54,29 +51,122 @@
if self.framestackdepth > self.space.sys.recursionlimit:
raise OperationError(self.space.w_RuntimeError,
self.space.wrap("maximum recursion depth exceeded"))
+ self._chain(frame)
+
+ def leave(self, frame):
+ if self.profilefunc:
+ self._trace(frame, 'leaveframe', self.space.w_None)
+
+ self._unchain(frame)
+
+ if self.w_tracefunc is not None and not frame.hide():
+ self.space.frame_trace_action.fire()
+
+ # ________________________________________________________________
+ # the methods below are used for chaining frames in JIT-friendly way
+ # part of that stuff is obscure
+
+ def _init_frame_chain(self):
+ # 'some_frame' points to any frame from this thread's frame stack
+ # (although in general it should point to the top one).
+ self.some_frame = None
+ self.framestackdepth = 0
+
+ @staticmethod
+ def _init_chaining_attributes(frame):
+ """
+ explanation of the f_back handling:
+ -----------------------------------
+
+ in the non-JIT case, the frames simply form a doubly linked list via the
+ attributes f_back_some and f_forward.
+
+ When the JIT is used, things become more complex, as functions can be
+ inlined into each other. In this case a frame chain can look like this:
+
+ +---------------+
+ | real_frame |
+ +---------------+
+ | ^
+ | f_back_some
+ | |
+ | | f_forward
+ | +--------------+
+ | | virtual frame|
+ | +--------------+
+ | ^
+ | | f_forward
+ | +--------------+
+ | | virtual frame|
+ | +--------------+
+ | ^
+ | |
+ v | f_forward
+ +---------------+
+ | real_frame |
+ +---------------+
+ |
+ |
+ v
+ ...
+
+ This ensures that the virtual frames don't escape via the f_back of the
+ real frames. For the same reason, the executioncontext's some_frame
+ attribute should only point to real frames.
+
+ All places where a frame can become accessed from applevel-code (like
+ sys._getframe and traceback catching) need to call force_f_back to ensure
+ that the intermediate virtual frames are forced to be real ones.
+
+ """
+ frame.f_back_some = None
+ frame.f_forward = None
+ frame.f_back_forced = False
+
+ def _chain(self, frame):
self.framestackdepth += 1
#
frame.f_back_some = self.some_frame
curtopframe = self.gettopframe()
if curtopframe is not None:
curtopframe.f_forward = frame
- if not we_are_jitted():
+ if not self._we_are_jitted():
self.some_frame = frame
- def leave(self, frame):
- if self.profilefunc:
- self._trace(frame, 'leaveframe', self.space.w_None)
-
+ def _unchain(self, frame):
#assert frame is self.gettopframe() --- slowish
f_back = frame.f_back()
if f_back is not None:
f_back.f_forward = None
- if not we_are_jitted() or self.some_frame is frame:
+ if not self._we_are_jitted() or self.some_frame is frame:
self.some_frame = frame.f_back_some
self.framestackdepth -= 1
-
- if self.w_tracefunc is not None and not frame.hide():
- self.space.frame_trace_action.fire()
+
+ @staticmethod
+ def _extract_back_from_frame(frame):
+ back_some = frame.f_back_some
+ if frame.f_back_forced:
+ # don't check back_some.f_forward in this case
+ return back_some
+ if back_some is None:
+ return None
+ while back_some.f_forward is not frame:
+ back_some = back_some.f_forward
+ return back_some
+
+ @staticmethod
+ def _force_back_of_frame(frame):
+ orig_frame = frame
+ while frame is not None and not frame.f_back_forced:
+ frame.f_back_some = f_back = ExecutionContext._extract_back_from_frame(frame)
+ frame.f_back_forced = True
+ frame = f_back
+ return orig_frame.f_back_some
+
+ _we_are_jitted = staticmethod(we_are_jitted) # indirection for testing
+
+ # the methods above are used for chaining frames in JIT-friendly way
+ # ________________________________________________________________
class Subcontext(object):
Modified: pypy/branch/spine-of-frames/pypy/interpreter/pyframe.py
==============================================================================
--- pypy/branch/spine-of-frames/pypy/interpreter/pyframe.py (original)
+++ pypy/branch/spine-of-frames/pypy/interpreter/pyframe.py Fri Sep 4 11:41:23 2009
@@ -5,6 +5,7 @@
from pypy.interpreter import eval, baseobjspace, pycode
from pypy.interpreter.argument import Arguments, ArgumentsFromValuestack
from pypy.interpreter.error import OperationError
+from pypy.interpreter.executioncontext import ExecutionContext
from pypy.interpreter import pytraceback
import opcode
from pypy.rlib.objectmodel import we_are_translated, instantiate
@@ -34,60 +35,12 @@
* 'valuestack_w', 'blockstack', control the interpretation
"""
- """
- explanation of the f_back handling:
- -----------------------------------
-
- in the non-JIT case, the frames simply form a doubly linked list via the
- attributes f_back_some and f_forward.
-
- When the JIT is used, things become more complex, as functions can be
- inlined into each other. In this case a frame chain can look like this:
-
- +---------------+
- | real_frame |
- +---------------+
- | ^
- | f_back_some
- | |
- | | f_forward
- | +--------------+
- | | virtual frame|
- | +--------------+
- | ^
- | | f_forward
- | +--------------+
- | | virtual frame|
- | +--------------+
- | ^
- | |
- v | f_forward
- +---------------+
- | real_frame |
- +---------------+
- |
- |
- v
- ...
-
- This ensures that the virtual frames don't escape via the f_back of the
- real frames. For the same reason, the executioncontext's some_frame
- attribute should only point to real frames.
-
- All places where a frame can become accessed from applevel-code (like
- sys._getframe and traceback catching) need to call force_f_back to ensure
- that the intermediate virtual frames are forced to be real ones.
-
- """
__metaclass__ = extendabletype
frame_finished_execution = False
last_instr = -1
last_exception = None
- f_back_some = None # these two should be modified together
- f_forward = None # they make a sort of doubly-linked list
- f_back_forced = False
w_f_trace = None
# For tracing
instr_lb = 0
@@ -111,6 +64,7 @@
self.fastlocals_w = [None]*self.numlocals
make_sure_not_resized(self.fastlocals_w)
self.f_lineno = code.co_firstlineno
+ ExecutionContext._init_chaining_attributes(self)
def get_builtin(self):
if self.space.config.objspace.honor__builtins__:
@@ -457,23 +411,10 @@
pass
def f_back(self):
- back_some = self.f_back_some
- if self.f_back_forced:
- # don't check back_some.f_forward in this case
- return back_some
- if back_some is None:
- return None
- while back_some.f_forward is not self:
- back_some = back_some.f_forward
- return back_some
-
- def force_f_back(self):
- self.f_back_some = f_back = self.f_back()
- self.f_back_forced = True
- if f_back is not None:
- f_back.force_f_back()
- return f_back
+ return ExecutionContext._extract_back_from_frame(self)
+ def force_f_back(self):
+ return ExecutionContext._force_back_of_frame(self)
### line numbers ###
Modified: pypy/branch/spine-of-frames/pypy/interpreter/test/test_executioncontext.py
==============================================================================
--- pypy/branch/spine-of-frames/pypy/interpreter/test/test_executioncontext.py (original)
+++ pypy/branch/spine-of-frames/pypy/interpreter/test/test_executioncontext.py Fri Sep 4 11:41:23 2009
@@ -1,5 +1,6 @@
import py
from pypy.interpreter import executioncontext
+from pypy.interpreter.executioncontext import ExecutionContext
from pypy.conftest import gettestobjspace
class Finished(Exception):
@@ -219,3 +220,216 @@
""")
events = space.unwrap(w_events)
assert [i[0] for i in events] == ['c_call', 'c_return', 'c_call']
+
+class TestFrameChaining(object):
+ class EC(ExecutionContext):
+ def __init__(self, jitted=False):
+ self.jitted = jitted
+ self._init_frame_chain()
+
+ def _we_are_jitted(self):
+ return self.jitted
+
+ class Frame(object):
+ def __init__(self):
+ ExecutionContext._init_chaining_attributes(self)
+
+ def f_back(self):
+ return ExecutionContext._extract_back_from_frame(self)
+
+ def force_f_back(self):
+ return ExecutionContext._force_back_of_frame(self)
+
+ def test_frame_chain(self):
+
+ ec = self.EC()
+
+ assert ec.some_frame is None
+ assert ec.framestackdepth == 0
+
+ frame = self.Frame()
+ ec._chain(frame)
+ assert ec.some_frame is frame
+ assert ec.framestackdepth == 1
+ assert frame.f_back_some is None
+ assert frame.f_forward is None
+
+ frame2 = self.Frame()
+ ec._chain(frame2)
+ assert ec.some_frame is frame2
+ assert ec.framestackdepth == 2
+ assert frame2.f_back_some is frame
+ assert frame.f_forward is frame2
+ assert frame2.f_forward is None
+
+ frame3 = self.Frame()
+ ec._chain(frame3)
+ assert ec.some_frame is frame3
+ assert frame3.f_back_some is frame2
+ assert frame2.f_forward is frame3
+ # now we should unchain
+
+ assert frame3.f_back() is frame2
+ ec._unchain(frame3)
+ assert ec.some_frame is frame2
+ assert ec.framestackdepth == 2
+ assert frame2.f_forward is None
+ assert frame3.f_back_some is frame2
+
+ assert frame2.f_back() is frame
+ ec._unchain(frame2)
+ assert ec.some_frame is frame
+ assert ec.framestackdepth == 1
+ assert frame.f_forward is None
+ assert frame2.f_back_some is frame
+
+ assert frame.f_back() is None
+ ec._unchain(frame)
+ assert ec.some_frame is None
+ assert ec.framestackdepth == 0
+ assert frame.f_back_some is None
+
+ # we want a simplified copy of the previous tests and do a force on frame2 ok
+ def test_frame_chain_forced(self):
+
+ ec = self.EC()
+
+ frame = self.Frame()
+ ec._chain(frame)
+
+ frame2 = self.Frame()
+ ec._chain(frame2)
+ assert ec.some_frame is frame2
+ assert ec.framestackdepth == 2
+ assert frame2.f_back_some is frame
+ assert frame.f_forward is frame2
+ assert frame2.f_forward is None
+ res = frame2.force_f_back()
+ assert res is frame
+ assert frame.f_back_forced
+
+ frame3 = self.Frame()
+ ec._chain(frame3)
+ # now we should unchain
+
+ assert frame3.f_back() is frame2
+ ec._unchain(frame3)
+ assert ec.some_frame is frame2
+ assert frame3.f_back_some is frame2
+
+ assert frame2.f_back() is frame
+ ec._unchain(frame2)
+ assert frame2.f_back_some is frame
+
+ assert frame.f_back() is None
+ ec._unchain(frame)
+ assert ec.some_frame is None
+ assert frame.f_back_some is None
+
+ assert frame2.f_back() is frame
+ assert frame.f_back() is None
+
+ def test_frame_chain_jitted(self):
+
+ ec = self.EC()
+
+ assert ec.some_frame is None
+ assert ec.framestackdepth == 0
+
+ frame = self.Frame()
+ ec._chain(frame)
+ assert ec.some_frame is frame
+ assert ec.framestackdepth == 1
+ assert frame.f_back_some is None
+ assert frame.f_forward is None
+
+ ec.jitted = True
+ frame2 = self.Frame()
+ ec._chain(frame2)
+ assert ec.some_frame is frame
+ assert ec.framestackdepth == 2
+ assert frame2.f_back_some is frame
+ assert frame.f_forward is frame2
+ assert frame2.f_forward is None
+
+ # recursive enter/leave seen by the jit
+ frame3 = self.Frame()
+ ec._chain(frame3)
+ assert ec.some_frame is frame
+ assert frame3.f_back_some is frame
+ assert frame2.f_forward is frame3
+ # now we should unchain
+
+ assert frame3.f_back() is frame2
+ ec._unchain(frame3)
+ assert ec.some_frame is frame
+ assert ec.framestackdepth == 2
+ assert frame2.f_forward is None
+ assert frame3.f_back_some is frame
+
+ # recursive enter/leave not seen by the jit
+ ec.jitted = False
+ ec._chain(frame3)
+ assert ec.some_frame is frame3
+ assert frame3.f_back_some is frame
+ assert frame2.f_forward is frame3 # this btw is the bit that may be problematic xxx
+ # now we should unchain
+
+ assert frame3.f_back() is frame2
+ ec._unchain(frame3)
+ assert ec.some_frame is frame
+ assert ec.framestackdepth == 2
+ assert frame2.f_forward is None
+ assert frame3.f_back_some is frame
+ ec.jitted = True
+
+ assert frame2.f_back() is frame
+ ec._unchain(frame2)
+ assert ec.some_frame is frame
+ assert ec.framestackdepth == 1
+ assert frame.f_forward is None
+ assert frame2.f_back_some is frame
+
+ ec.jitted = False
+ assert frame.f_back() is None
+ ec._unchain(frame)
+ assert ec.some_frame is None
+ assert ec.framestackdepth == 0
+ assert frame.f_back_some is None
+
+
+ def test_frame_chain_jitted_forced(self):
+
+ ec = self.EC()
+
+ assert ec.some_frame is None
+ assert ec.framestackdepth == 0
+
+ frame = self.Frame()
+ ec._chain(frame)
+
+ ec.jitted = True
+ frame2 = self.Frame()
+ ec._chain(frame2)
+
+ # recursive enter/leave seen by the jit
+ frame3 = self.Frame()
+ ec._chain(frame3)
+ res = frame3.force_f_back()
+ assert res is frame2
+
+ assert frame3.f_back() is frame2
+ ec._unchain(frame3)
+
+ assert frame2.f_back() is frame
+ ec._unchain(frame2)
+ ec.jitted = False
+ assert frame.f_back() is None
+ ec._unchain(frame)
+
+ assert frame3.f_back() is frame2
+ assert frame2.f_back() is frame
+ assert frame.f_back() is None
+ # cool yes
+
+
More information about the Pypy-commit
mailing list