[pypy-svn] r28366 - in pypy/dist/pypy: module/stackless translator/stackless translator/stackless/test

mwh at codespeak.net mwh at codespeak.net
Tue Jun 6 13:19:47 CEST 2006


Author: mwh
Date: Tue Jun  6 13:19:45 2006
New Revision: 28366

Added:
   pypy/dist/pypy/translator/stackless/test/test_coroutine_reconstruction.py   (contents, props changed)
Modified:
   pypy/dist/pypy/module/stackless/interp_coroutine.py
   pypy/dist/pypy/translator/stackless/code.py
   pypy/dist/pypy/translator/stackless/transform.py
Log:
(mwh, pedronis)
A test and implementation of building a coroutine by hand using the
resume_point machinery.
* add two resume_points in interp_coroutine.
* add names (for passing to resume_state_create) to all the prebuilt
  restart points.
  * this required making the stackless helpers and regular code use the same
    frame types when the saved state erases to the same types.
  * we advise against reading some of the new code in
    StacklessTransfomer.__init__ (will clean up soon).


Modified: pypy/dist/pypy/module/stackless/interp_coroutine.py
==============================================================================
--- pypy/dist/pypy/module/stackless/interp_coroutine.py	(original)
+++ pypy/dist/pypy/module/stackless/interp_coroutine.py	Tue Jun  6 13:19:45 2006
@@ -30,7 +30,7 @@
 """
 
 from pypy.interpreter.baseobjspace import Wrappable
-from pypy.rpython.rstack import yield_current_frame_to_caller
+from pypy.rpython.rstack import yield_current_frame_to_caller, resume_point
 
 import sys, os
 
@@ -127,6 +127,7 @@
         try:
             costate.do_things_to_do()
             thunk.call()
+            resume_point("coroutine__bind", self, state)
         except CoroutineExit:
             # ignore a shutdown exception
             pass
@@ -146,6 +147,7 @@
             raise CoroutineDamage
         state = self.costate
         incoming_frame = state.update(self).switch()
+        resume_point("coroutine_switch", self, state, returns=incoming_frame)
         left = state.last
         left.frame = incoming_frame
         left.goodbye()

Modified: pypy/dist/pypy/translator/stackless/code.py
==============================================================================
--- pypy/dist/pypy/translator/stackless/code.py	(original)
+++ pypy/dist/pypy/translator/stackless/code.py	Tue Jun  6 13:19:45 2006
@@ -181,17 +181,14 @@
 INDEX_CAPTURE = frame.RestartInfo.add_prebuilt(ll_stack_capture,
                                                [EMPTY_STATE])
 
-RESUME_AFTER_STATE = frame.make_state_header_type('resume_after_state',
-                                                  ('c', lltype.Ptr(STATE_HEADER)),)
-
 def resume_after_void(state, retvalue):
     if global_state.restart_substate == -1:
         # normal entry point for a call to state.switch()
         # first unwind the stack
         u = UnwindException()
-        s = lltype.malloc(RESUME_AFTER_STATE)
+        s = lltype.malloc(SWITCH_STATE)
         s.header.f_restart = INDEX_RESUME_AFTER_VOID
-        s.c = lltype.cast_opaque_ptr(lltype.Ptr(STATE_HEADER), state)
+        s.c = lltype.cast_opaque_ptr(SAVED_REFERENCE, state)
         add_frame_state(u, s.header)
         raise u
     elif global_state.restart_substate == 0:
@@ -201,8 +198,8 @@
         # the 'targetstate' local is garbage here, it must be read back from
         # 's.c' where we saved it by the normal entry point above
         mystate = global_state.top
-        s = lltype.cast_pointer(lltype.Ptr(RESUME_AFTER_STATE), mystate)
-        targetstate = s.c
+        s = lltype.cast_pointer(lltype.Ptr(SWITCH_STATE), mystate)
+        targetstate = lltype.cast_opaque_ptr(lltype.Ptr(STATE_HEADER), s.c)
         resume_bottom = targetstate
         while resume_bottom.f_back:
              resume_bottom = resume_bottom.f_back
@@ -212,7 +209,7 @@
 
 resume_after_void.stackless_explicit = True
 INDEX_RESUME_AFTER_VOID = frame.RestartInfo.add_prebuilt(resume_after_void,
-                                                         [RESUME_AFTER_STATE,
+                                                         [SWITCH_STATE,
                                                           EMPTY_STATE])
 
 
@@ -221,9 +218,9 @@
         # normal entry point for a call to state.switch()
         # first unwind the stack
         u = UnwindException()
-        s = lltype.malloc(RESUME_AFTER_STATE)
+        s = lltype.malloc(SWITCH_STATE)
         s.header.f_restart = INDEX_RESUME_AFTER_RAISING
-        s.c = lltype.cast_opaque_ptr(lltype.Ptr(STATE_HEADER), state)
+        s.c = lltype.cast_opaque_ptr(SAVED_REFERENCE, state)
         add_frame_state(u, s.header)
         global_state.exception = exception
         raise u
@@ -234,8 +231,8 @@
         # the 'targetstate' local is garbage here, it must be read back from
         # 's.c' where we saved it by the normal entry point above
         mystate = global_state.top
-        s = lltype.cast_pointer(lltype.Ptr(RESUME_AFTER_STATE), mystate)
-        targetstate = s.c
+        s = lltype.cast_pointer(lltype.Ptr(SWITCH_STATE), mystate)
+        targetstate = lltype.cast_opaque_ptr(lltype.Ptr(STATE_HEADER), s.c)
         resume_bottom = targetstate
         while resume_bottom.f_back:
              resume_bottom = resume_bottom.f_back
@@ -245,7 +242,7 @@
 
 resume_after_raising.stackless_explicit = True
 INDEX_RESUME_AFTER_RAISING = frame.RestartInfo.add_prebuilt(resume_after_raising,
-                                                            [RESUME_AFTER_STATE,
+                                                            [SWITCH_STATE,
                                                              EMPTY_STATE])
 
 template = """\
@@ -254,9 +251,9 @@
         # normal entry point for a call to state.switch()
         # first unwind the stack
         u = UnwindException()
-        s = lltype.malloc(RESUME_AFTER_STATE)
+        s = lltype.malloc(SWITCH_STATE)
         s.header.f_restart = INDEX_RESUME_AFTER_%(TYPENAME)s
-        s.c = lltype.cast_opaque_ptr(lltype.Ptr(STATE_HEADER), state)
+        s.c = lltype.cast_opaque_ptr(SAVED_REFERENCE, state)
         global_state.retval_%(typename)s = retvalue
         add_frame_state(u, s.header)
         raise u
@@ -267,8 +264,8 @@
         # the 'targetstate' local is garbage here, it must be read back from
         # 's.c' where we saved it by the normal entry point above
         mystate = global_state.top
-        s = lltype.cast_pointer(lltype.Ptr(RESUME_AFTER_STATE), mystate)
-        targetstate = s.c
+        s = lltype.cast_pointer(lltype.Ptr(SWITCH_STATE), mystate)
+        targetstate = lltype.cast_opaque_ptr(lltype.Ptr(STATE_HEADER), s.c)
         resume_bottom = targetstate
         while resume_bottom.f_back:
              resume_bottom = resume_bottom.f_back
@@ -279,7 +276,7 @@
 
 resume_after_%(typename)s.stackless_explicit = True
 INDEX_RESUME_AFTER_%(TYPENAME)s = frame.RestartInfo.add_prebuilt(resume_after_%(typename)s,
-                                                         [RESUME_AFTER_STATE,
+                                                         [SWITCH_STATE,
                                                           EMPTY_STATE])
 """
 

Added: pypy/dist/pypy/translator/stackless/test/test_coroutine_reconstruction.py
==============================================================================
--- (empty file)
+++ pypy/dist/pypy/translator/stackless/test/test_coroutine_reconstruction.py	Tue Jun  6 13:19:45 2006
@@ -0,0 +1,61 @@
+from pypy.module.stackless import interp_coroutine
+from pypy.rpython import rstack
+from pypy.rpython.rstack import resume_state_create
+from pypy.translator.stackless.test.test_transform import llinterp_stackless_function
+from pypy.rpython.lltypesystem.lloperation import llop
+from pypy.rpython.lltypesystem import lltype
+
+class TestCoroutineReconstruction:
+
+    def setup_meth(self):
+        interp_coroutine.costate.__init__()
+
+    def test_simple_ish(self):
+
+        output = []
+        def f(coro, n, x):
+            if n == 0:
+                coro.switch()
+                rstack.resume_point("f_0")
+                return
+            f(coro, n-1, 2*x)
+            rstack.resume_point("f_1", coro, n, x)
+            output.append(x)
+
+        class T(interp_coroutine.AbstractThunk):
+            def __init__(self, arg_coro, arg_n, arg_x):
+                self.arg_coro = arg_coro
+                self.arg_n = arg_n
+                self.arg_x = arg_x
+            def call(self):
+                f(self.arg_coro, self.arg_n, self.arg_x)
+
+        def example():
+            main_coro = interp_coroutine.costate.main
+            sub_coro = interp_coroutine.Coroutine()
+            thunk_f = T(main_coro, 5, 1)
+            sub_coro.bind(thunk_f)
+            sub_coro.switch()
+
+            new_coro = interp_coroutine.Coroutine()
+            new_thunk_f = T(main_coro, 5, 1)
+            new_coro.bind(new_thunk_f)
+
+            bottom = resume_state_create(None, "yield_current_frame_to_caller_1")
+            _bind_frame = resume_state_create(bottom, "coroutine__bind", new_coro, interp_coroutine.costate)
+            f_frame_1 = resume_state_create(_bind_frame, "f_1", main_coro, 5, 1)
+            f_frame_2 = resume_state_create(f_frame_1, "f_1", main_coro, 4, 2)
+            f_frame_3 = resume_state_create(f_frame_2, "f_1", main_coro, 3, 4)
+            f_frame_4 = resume_state_create(f_frame_3, "f_1", main_coro, 2, 8)
+            f_frame_5 = resume_state_create(f_frame_4, "f_1", main_coro, 1, 16)
+            f_frame_0 = resume_state_create(f_frame_5, "f_0")
+            switch_frame = resume_state_create(f_frame_0, "coroutine_switch", new_coro, interp_coroutine.costate)
+
+            new_coro.frame = switch_frame
+
+            new_coro.switch()
+            return output == [16, 8, 4, 2, 1]
+
+        res = llinterp_stackless_function(example)
+        assert res == 1
+

Modified: pypy/dist/pypy/translator/stackless/transform.py
==============================================================================
--- pypy/dist/pypy/translator/stackless/transform.py	(original)
+++ pypy/dist/pypy/translator/stackless/transform.py	Tue Jun  6 13:19:45 2006
@@ -83,11 +83,11 @@
     def __init__(self):
         self.frametypes = {}
 
-    def frame_type_for_vars(self, vars):
+    def _key_fieldnames_for_types(self, types):
         fieldnames = []
         counts = {}
-        for v in vars:
-            t = storage_type(v.concretetype)
+        for tt in types:
+            t = storage_type(tt)
             if t is lltype.Void:
                 fieldnames.append(None)
             else:
@@ -95,12 +95,31 @@
                 fieldnames.append('state_%s_%d' % (STORAGE_FIELDS[t], n))
                 counts[t] = n + 1
         key = lltype.frozendict(counts)
+        return key, fieldnames
+        
+
+    def frame_type_for_vars(self, vars):
+        key, fieldnames = self._key_fieldnames_for_types([v.concretetype for v in vars])
         if key in self.frametypes:
             T = self.frametypes[key]
+            it = iter(T._names[1:])
+            rfieldnames = []
+            for name in fieldnames:
+                if name is None:
+                    rfieldnames.append(None)
+                else:
+                    rfieldnames.append(it.next())
+            try:
+                it.next()
+            except StopIteration:
+                pass
+            else:
+                assert False, "field name count mismatch"
+            return T, rfieldnames
         else:
             fields = []
             for t in STORAGE_TYPES:
-                for j in range(counts.get(t, 0)):
+                for j in range(key.get(t, 0)):
                     fields.append(('state_%s_%d' % (STORAGE_FIELDS[t], j), t))
             T = frame.make_state_header_type("FrameState", *fields)
             self.frametypes[key] = T
@@ -299,8 +318,25 @@
         
         self.symbolic_restart_numbers = {}
 
-        # register the prebuilt restartinfos
+        # register the prebuilt restartinfos & give them names for use
+        # with resume_state_create
+        # the mauling of frame_typer internals should be a method on FrameTyper.
         for restartinfo in frame.RestartInfo.prebuilt:
+            name = restartinfo.func_or_graph.__name__
+            for i in range(len(restartinfo.frame_types)):
+                label = name + '_' + str(i)
+                assert label not in self.symbolic_restart_numbers
+                # XXX we think this is right:
+                self.symbolic_restart_numbers[label] = SymbolicRestartNumber(
+                    label, len(self.masterarray1) + i)
+                frame_type = restartinfo.frame_types[i]
+                self.explicit_resume_point_data[label] = frame_type
+                key, fieldnames = self.frametyper._key_fieldnames_for_types(
+                    [frame_type._flds[n] for n in frame_type._names[1:]])
+                assert len(fieldnames) <= 1, "nasty field ordering issues need to be solved XXX mwh, pedronis"
+                if key in self.frametyper.frametypes:
+                    assert self.frametyper.frametypes[key] is frame_type
+                self.frametyper.frametypes[key] = frame_type
             self.register_restart_info(restartinfo)
 
     def transform_all(self):
@@ -475,12 +511,12 @@
         for a in args:
             if a not in parms:
                 raise Exception, "not covered needed value at resume_point"
-            if parms[0] is not None: # returns= case
-                res = parms[0]
-                args = [arg for arg in args if arg is not res]
-            else:
-                args = args
-                res = op.result
+        if parms[0] is not None: # returns= case
+            res = parms[0]
+            args = [arg for arg in args if arg is not res]
+        else:
+            args = args
+            res = op.result
 
         (frame_type,
          fieldnames) = self.frametyper.frame_type_for_vars(parms[1:])



More information about the Pypy-commit mailing list