[pypy-svn] r18928 - in pypy/dist/pypy: annotation rpython rpython/module translator translator/c translator/c/src translator/c/test

arigo at codespeak.net arigo at codespeak.net
Tue Oct 25 13:32:18 CEST 2005


Author: arigo
Date: Tue Oct 25 13:32:15 2005
New Revision: 18928

Added:
   pypy/dist/pypy/rpython/rstack.py
   pypy/dist/pypy/translator/c/test/test_stackless.py
      - copied, changed from r18743, pypy/dist/pypy/translator/c/test/test_standalone.py
Modified:
   pypy/dist/pypy/annotation/builtin.py
   pypy/dist/pypy/rpython/extfunctable.py
   pypy/dist/pypy/rpython/module/ll_stack.py
   pypy/dist/pypy/rpython/module/ll_stackless.py
   pypy/dist/pypy/rpython/objectmodel.py
   pypy/dist/pypy/rpython/rbuiltin.py
   pypy/dist/pypy/rpython/rspecialcase.py
   pypy/dist/pypy/translator/c/extfunc.py
   pypy/dist/pypy/translator/c/src/ll_stackless.h
   pypy/dist/pypy/translator/c/stackless.py
   pypy/dist/pypy/translator/c/test/test_standalone.py
   pypy/dist/pypy/translator/transform.py
Log:
Capturing stack frame tops:

* yield_current_frame_to_caller() as described in
http://codespeak.net/svn/pypy/extradoc/sprintinfo/paris/stackless-discussion.txt

* the existing and the new RPython functions about stack manipulation
  are now in their own rpython/rstack.py

* yield_current_frame_to_caller() turns into a low-level operation of
  the same name, which is special-cased by translator/c/stackless.py

* moved the stackless tests in their own translator/c/test/test_stackless.py

* C implementation: the 'yield_current_frame_to_caller' operation triggers
  a stack unwind but doesn't propagate the unwind exception -- instead, it
  grabs the new stack top, link it a bit to make it stand-alone, and returns it
  to the caller.  The switch() method of frame_stack_top objects unwinds the
  current stack completely and then sets the global variables in a state that
  fools the main loop into thinking that the next frame to run is precisely the
  one we are trying to switch to.



Modified: pypy/dist/pypy/annotation/builtin.py
==============================================================================
--- pypy/dist/pypy/annotation/builtin.py	(original)
+++ pypy/dist/pypy/annotation/builtin.py	Tue Oct 25 13:32:15 2005
@@ -9,12 +9,14 @@
 from pypy.annotation.model import SomeUnicodeCodePoint, SomeAddress
 from pypy.annotation.model import SomeFloat, unionof
 from pypy.annotation.model import SomePBC, SomeInstance, SomeDict
+from pypy.annotation.model import SomeExternalObject
 from pypy.annotation.model import annotation_to_lltype, lltype_to_annotation
 from pypy.annotation.model import add_knowntypedata
 from pypy.annotation.bookkeeper import getbookkeeper
 from pypy.objspace.flow.model import Constant
 import pypy.rpython.rarithmetic
 import pypy.rpython.objectmodel
+import pypy.rpython.rstack
 
 # convenience only!
 def immutablevalue(x):
@@ -256,6 +258,9 @@
 
 def robjmodel_keepalive_until_here(*args_s):
     return immutablevalue(None)
+
+def rstack_yield_current_frame_to_caller():
+    return SomeExternalObject(pypy.rpython.rstack.frame_stack_top)
     
 
 ##def rarith_ovfcheck(s_obj):
@@ -299,6 +304,8 @@
 BUILTIN_ANALYZERS[pypy.rpython.objectmodel.r_dict] = robjmodel_r_dict
 BUILTIN_ANALYZERS[pypy.rpython.objectmodel.hlinvoke] = robjmodel_hlinvoke
 BUILTIN_ANALYZERS[pypy.rpython.objectmodel.keepalive_until_here] = robjmodel_keepalive_until_here
+BUILTIN_ANALYZERS[pypy.rpython.rstack.yield_current_frame_to_caller] = (
+    rstack_yield_current_frame_to_caller)
 
 BUILTIN_ANALYZERS[Exception.__init__.im_func] = exception_init
 BUILTIN_ANALYZERS[OSError.__init__.im_func] = exception_init

Modified: pypy/dist/pypy/rpython/extfunctable.py
==============================================================================
--- pypy/dist/pypy/rpython/extfunctable.py	(original)
+++ pypy/dist/pypy/rpython/extfunctable.py	Tue Oct 25 13:32:15 2005
@@ -221,11 +221,14 @@
 
 # ___________________________________________________________
 # stackless
-from pypy.rpython import objectmodel
-declare(objectmodel.stack_frames_depth, int, 'll_stackless/stack_frames_depth')
-declare(objectmodel.stack_too_big, bool, 'll_stack/too_big')
-declare(objectmodel.stack_check, noneannotation, 'll_stack/check')
-declare(objectmodel.stack_unwind, noneannotation, 'll_stack/unwind')
+from pypy.rpython import rstack
+declare(rstack.stack_frames_depth, int, 'll_stackless/stack_frames_depth')
+declare(rstack.stack_too_big, bool, 'll_stack/too_big')
+declare(rstack.stack_check, noneannotation, 'll_stack/check')
+declare(rstack.stack_unwind, noneannotation, 'll_stack/unwind')
+frametop_type_info = declareptrtype(rstack.frame_stack_top, 'frame_stack_top',
+                                        switch = (rstack.frame_stack_top,
+                                                  'll_stackless/switch'))
 
 # ___________________________________________________________
 # the exceptions that can be implicitely raised by some operations

Modified: pypy/dist/pypy/rpython/module/ll_stack.py
==============================================================================
--- pypy/dist/pypy/rpython/module/ll_stack.py	(original)
+++ pypy/dist/pypy/rpython/module/ll_stack.py	Tue Oct 25 13:32:15 2005
@@ -1,11 +1,11 @@
-from pypy.rpython import objectmodel
+from pypy.rpython import rstack
 
 def ll_stack_too_big():
-    return objectmodel.stack_too_big()
+    return rstack.stack_too_big()
 ll_stack_too_big.suggested_primitive = True
 
 def ll_stack_unwind():
-    objectmodel.stack_unwind()
+    rstack.stack_unwind()
 ll_stack_unwind.suggested_primitive = True
 
 def ll_stack_check():

Modified: pypy/dist/pypy/rpython/module/ll_stackless.py
==============================================================================
--- pypy/dist/pypy/rpython/module/ll_stackless.py	(original)
+++ pypy/dist/pypy/rpython/module/ll_stackless.py	Tue Oct 25 13:32:15 2005
@@ -1,5 +1,19 @@
-from pypy.rpython import objectmodel
+from pypy.rpython import rstack, lltype, extfunctable
+from pypy.rpython.module.support import from_opaque_object, to_opaque_object
+
+FRAMETOPTYPE = extfunctable.frametop_type_info.get_lltype()
+
 
 def ll_stackless_stack_frames_depth():
-    return objectmodel.stack_frames_depth()
+    return rstack.stack_frames_depth()
 ll_stackless_stack_frames_depth.suggested_primitive = True
+
+
+def ll_stackless_switch(opaqueframetop):
+    frametop = from_opaque_object(opaqueframetop)
+    newframetop = frametop.switch()
+    if newframetop is None:
+        return lltype.nullptr(FRAMETOPTYPE)
+    else:
+        return to_opaque_object(newframetop)
+ll_stackless_switch.suggested_primitive = True

Modified: pypy/dist/pypy/rpython/objectmodel.py
==============================================================================
--- pypy/dist/pypy/rpython/objectmodel.py	(original)
+++ pypy/dist/pypy/rpython/objectmodel.py	Tue Oct 25 13:32:15 2005
@@ -3,7 +3,7 @@
 RPython-compliant way.
 """
 
-import new, inspect
+import new
 
 
 def instantiate(cls):
@@ -38,22 +38,6 @@
 def hlinvoke(repr, llcallable, *args):
     raise TypeError, "hlinvoke is meant to be rtyped and not called direclty"
 
-def stack_unwind():
-    pass
-
-def stack_frames_depth():
-    return len(inspect.stack())
-
-def stack_too_big():
-    return False
-
-def stack_check():
-    if stack_too_big():
-        # stack_unwind implementation is different depending on if stackless
-        # is enabled. If it is it unwinds the stack, otherwise it simply
-        # raises a RuntimeError.
-        stack_unwind()
-
 # ____________________________________________________________
 
 

Modified: pypy/dist/pypy/rpython/rbuiltin.py
==============================================================================
--- pypy/dist/pypy/rpython/rbuiltin.py	(original)
+++ pypy/dist/pypy/rpython/rbuiltin.py	Tue Oct 25 13:32:15 2005
@@ -1,7 +1,7 @@
 from pypy.annotation.pairtype import pairtype
 from pypy.annotation import model as annmodel
 from pypy.rpython import lltype
-from pypy.rpython import rarithmetic, objectmodel
+from pypy.rpython import rarithmetic, objectmodel, rstack
 from pypy.rpython.rtyper import TyperError
 from pypy.rpython.rrange import rtype_builtin_range, rtype_builtin_xrange 
 from pypy.rpython.rmodel import Repr, TyperError, IntegerRepr, Constant
@@ -189,6 +189,10 @@
 def rtype_we_are_translated(hop):
     return hop.inputconst(lltype.Bool, True)
 
+def rtype_yield_current_frame_to_caller(hop):
+    return hop.genop('yield_current_frame_to_caller', [],
+                     resulttype=hop.r_result)
+
 def rtype_hlinvoke(hop):
     _, s_repr = hop.r_s_popfirstarg()
     r_callable = s_repr.const
@@ -275,6 +279,8 @@
 BUILTIN_TYPER[rarithmetic.r_uint] = rtype_r_uint
 BUILTIN_TYPER[objectmodel.r_dict] = rtype_r_dict
 BUILTIN_TYPER[objectmodel.we_are_translated] = rtype_we_are_translated
+BUILTIN_TYPER[rstack.yield_current_frame_to_caller] = (
+    rtype_yield_current_frame_to_caller)
 
 BUILTIN_TYPER[objectmodel.hlinvoke] = rtype_hlinvoke
 

Modified: pypy/dist/pypy/rpython/rspecialcase.py
==============================================================================
--- pypy/dist/pypy/rpython/rspecialcase.py	(original)
+++ pypy/dist/pypy/rpython/rspecialcase.py	Tue Oct 25 13:32:15 2005
@@ -44,3 +44,7 @@
 def rtype_override_to_opaque_object(hop, clsdef):
     return hop.genop('to_opaque_object_should_never_be_seen_by_the_backend',
                      [], resulttype=hop.r_result)
+
+def rtype_override_yield_current_frame_to_caller(hop, clsdef):
+    return hop.genop('yield_current_frame_to_caller', [], 
+                     resulttype=hop.r_result)

Added: pypy/dist/pypy/rpython/rstack.py
==============================================================================
--- (empty file)
+++ pypy/dist/pypy/rpython/rstack.py	Tue Oct 25 13:32:15 2005
@@ -0,0 +1,31 @@
+"""
+This file defines utilities for manipulating the stack in an
+RPython-compliant way, intended mostly for use by the Stackless PyPy.
+"""
+
+import inspect
+
+def stack_unwind():
+    pass
+
+def stack_frames_depth():
+    return len(inspect.stack())
+
+def stack_too_big():
+    return False
+
+def stack_check():
+    if stack_too_big():
+        # stack_unwind implementation is different depending on if stackless
+        # is enabled. If it is it unwinds the stack, otherwise it simply
+        # raises a RuntimeError.
+        stack_unwind()
+
+# ____________________________________________________________
+
+def yield_current_frame_to_caller():
+    raise NotImplementedError("only works in translated versions")
+
+class frame_stack_top(object):
+    def switch(self):
+        raise NotImplementedError("only works in translated versions")

Modified: pypy/dist/pypy/translator/c/extfunc.py
==============================================================================
--- pypy/dist/pypy/translator/c/extfunc.py	(original)
+++ pypy/dist/pypy/translator/c/extfunc.py	Tue Oct 25 13:32:15 2005
@@ -54,6 +54,7 @@
     ll_thread.ll_releaselock:      'LL_thread_releaselock',
     ll_thread.ll_thread_start:     'LL_thread_start',
     ll_thread.ll_thread_get_ident: 'LL_thread_get_ident',
+    ll_stackless.ll_stackless_switch:             'LL_stackless_switch',
     ll_stackless.ll_stackless_stack_frames_depth: 'LL_stackless_stack_frames_depth',
     ll_stack.ll_stack_unwind: 'LL_stack_unwind',
     ll_stack.ll_stack_too_big: 'LL_stack_too_big',

Modified: pypy/dist/pypy/translator/c/src/ll_stackless.h
==============================================================================
--- pypy/dist/pypy/translator/c/src/ll_stackless.h	(original)
+++ pypy/dist/pypy/translator/c/src/ll_stackless.h	Tue Oct 25 13:32:15 2005
@@ -13,6 +13,11 @@
   int state;
 } slp_frame_t;
 
+typedef struct {
+  slp_frame_t header;
+  void* p0;
+} slp_frame_1ptr_t;
+
 struct slp_state_decoding_entry_s {
   void *function;
   int signature;
@@ -33,6 +38,9 @@
 long LL_stackless_stack_frames_depth(void);
 void slp_main_loop(void);
 char LL_stackless_stack_too_big(void);
+struct RPyOpaque_frame_stack_top *slp_return_current_frame_to_caller(void);
+struct RPyOpaque_frame_stack_top *
+LL_stackless_switch(struct RPyOpaque_frame_stack_top *c);
 
 #ifndef PYPY_NOT_MAIN_FILE
 
@@ -67,6 +75,51 @@
     slp_frame_stack_top = NULL;
 }
 
+struct RPyOpaque_frame_stack_top *slp_return_current_frame_to_caller(void)
+{
+  slp_frame_t *result = slp_frame_stack_top;
+  assert(slp_frame_stack_top != NULL);
+  assert(slp_frame_stack_bottom != NULL);
+  slp_frame_stack_bottom->f_back = slp_new_frame(sizeof(slp_frame_t), 3);
+  slp_frame_stack_top = slp_frame_stack_bottom = NULL;  /* stop unwinding */
+  return (struct RPyOpaque_frame_stack_top *) result;
+}
+
+struct RPyOpaque_frame_stack_top *slp_end_of_yielding_function(void)
+{
+  assert(slp_frame_stack_top != NULL); /* can only be resumed from
+                                       slp_return_current_frame_to_caller() */
+  assert(slp_retval_voidptr != NULL);
+  slp_frame_stack_top = (slp_frame_t *) slp_retval_voidptr;
+  return NULL;
+}
+
+struct RPyOpaque_frame_stack_top *
+LL_stackless_switch(struct RPyOpaque_frame_stack_top *c)
+{
+	slp_frame_t *f;
+	slp_frame_t *result;
+	if (slp_frame_stack_top)
+		goto resume;
+
+	/* first, unwind the current stack */
+	f = slp_new_frame(sizeof(slp_frame_1ptr_t), 2);
+	((slp_frame_1ptr_t *) f)->p0 = c;
+	slp_frame_stack_top = slp_frame_stack_bottom = f;
+	return NULL;
+
+   resume:
+	/* ready to do the switch.  The current (old) frame_stack_top is
+	   f->f_back, which we store where it will be found immediately
+	   after the switch */
+	f = slp_frame_stack_top;
+	result = f->f_back;
+
+	/* grab the saved value of 'c' and do the switch */
+	slp_frame_stack_top = (slp_frame_t *) (((slp_frame_1ptr_t *) f)->p0);
+	return (struct RPyOpaque_frame_stack_top *) result;
+}
+
 
 /* example function for testing */
 
@@ -131,15 +184,20 @@
 	  }
 
           free(pending);  /* consumed by the previous call */
-          if (slp_frame_stack_bottom)
+          if (slp_frame_stack_top)
             break;
           if (!back)
             return;
           pending = back;
           slp_frame_stack_top = pending;
         }
-      assert(slp_frame_stack_bottom->f_back == NULL);
-      slp_frame_stack_bottom->f_back = back;
+      /* slp_frame_stack_bottom is usually non-NULL here, apart from
+         when returning from switch() */
+      if (slp_frame_stack_bottom != NULL)
+        {
+          assert(slp_frame_stack_bottom->f_back == NULL);
+          slp_frame_stack_bottom->f_back = back;
+        }
     }
 }
 

Modified: pypy/dist/pypy/translator/c/stackless.py
==============================================================================
--- pypy/dist/pypy/translator/c/stackless.py	(original)
+++ pypy/dist/pypy/translator/c/stackless.py	Tue Oct 25 13:32:15 2005
@@ -26,6 +26,12 @@
         self.registerunwindable('LL_stackless_stack_frames_depth',
                                 lltype.FuncType([], lltype.Signed),
                                 resume_points=1)
+        self.registerunwindable('LL_stackless_switch',
+                                lltype.FuncType([Address], Address),
+                                resume_points=1)
+        self.registerunwindable('slp_end_of_yielding_function',
+                                lltype.FuncType([], Address),
+                                resume_points=1)
 
     def registerunwindable(self, functionname, FUNC, resume_points):
         if resume_points >= 1:
@@ -181,7 +187,7 @@
         del self.savelines
         del self.resumeblocks
 
-    def check_directcall_result(self, op, err):
+    def check_directcall_result(self, op, err, specialreturnvalue=None):
         stacklessdata = self.db.stacklessdata
         block = self.currentblock
         curpos = block.operations.index(op)
@@ -216,7 +222,8 @@
         arguments = ['%d' % globalstatecounter] + vars
 
         savecall = 'save_%s(%s);' % (structname, ', '.join(arguments))
-        savecall += ' return %s;' % self.error_return_value()
+        returnvalue = specialreturnvalue or self.error_return_value()
+        savecall += ' return %s;' % returnvalue
         self.savelines.append('%s: %s' % (savelabel, savecall))
 
         # generate the resume block, e.g.
@@ -250,6 +257,17 @@
                                     resumelabel,
                                     exception_check)
 
+    def OP_YIELD_CURRENT_FRAME_TO_CALLER(self, op, err):
+        # special handling of this operation: call stack_unwind() to force the
+        # current frame to be saved into the heap, but don't propagate the
+        # unwind -- instead, capture it and return it normally
+        line = '/* yield_current_frame_to_caller */\n'
+        line += '%s = NULL;\n' % self.expr(op.result)
+        line += 'LL_stackless_stack_unwind();\n'
+        line += self.check_directcall_result(op, err,
+                    specialreturnvalue='slp_return_current_frame_to_caller()')
+        return line
+
 
 def signature_type(T):
     """Return T unless it's a pointer type, in which case we return a general

Modified: pypy/dist/pypy/translator/c/test/test_standalone.py
==============================================================================
--- pypy/dist/pypy/translator/c/test/test_standalone.py	(original)
+++ pypy/dist/pypy/translator/c/test/test_standalone.py	Tue Oct 25 13:32:15 2005
@@ -2,7 +2,6 @@
 from pypy.translator.tool.cbuild import build_executable
 from pypy.annotation.model import SomeList, SomeString
 from pypy.annotation.listdef import ListDef
-from pypy.rpython.objectmodel import stack_unwind, stack_frames_depth, stack_too_big
 import os
 
 
@@ -24,135 +23,3 @@
     cbuilder.compile()
     data = cbuilder.cmdexec('hi there')
     assert data.startswith('''hello world\nargument count: 2\n   'hi'\n   'there'\n''')
-
-
-def test_stack_depth():
-    def g1():
-        "just to check Void special cases around the code"
-    def g2(ignored):
-        g1()
-    def f(n):
-        g1()
-        if n > 0:
-            res = f(n-1)
-        else:
-            res = stack_frames_depth()
-        g2(g1)
-        return res
-
-    def fn():
-        count0 = f(0)
-        count10 = f(10)
-        return count10 - count0
-
-    data = wrap_stackless_function(fn)
-    assert data.strip() == '10'
-
-def test_stack_withptr():
-    def f(n):
-        if n > 0:
-            res = f(n-1)
-        else:
-            res = stack_frames_depth(), 1
-        return res
-
-    def fn():
-        count0, _ = f(0)
-        count10, _ = f(10)
-        return count10 - count0
-
-    data = wrap_stackless_function(fn)
-    assert data.strip() == '10'
-
-def test_stackless_manytimes():
-    def f(n):
-        if n > 0:
-            stack_frames_depth()
-            res = f(n-1)
-        else:
-            res = stack_frames_depth(), 1
-        return res
-
-    def fn():
-        count0, _ = f(0)
-        count10, _ = f(100)
-        return count10 - count0
-
-    data = wrap_stackless_function(fn)
-    assert data.strip() == '100'
-
-def test_stackless_arguments():
-    def f(n, d, t):
-        if n > 0:
-            res = f(n-1, d, t)
-        else:
-            res = stack_frames_depth(), d, t
-        return res
-
-    def fn():
-        count0, d, t = f(0, 5.5, (1, 2))
-        count10, d, t = f(10, 5.5, (1, 2))
-        return "[" + str(count10 - count0) + ", " + str(d) + ", " + str(t[0]) + ", " + str(t[1]) + "]"
-
-    data = wrap_stackless_function(fn)
-    assert eval(data) == [10, 5.5, 1, 2]
-
-
-def test_stack_too_big():
-    def f1():
-        return stack_too_big()
-    def f2():
-        return lst[1]()
-    def f3():
-        return lst[2]()
-    def f4():
-        return lst[3]()
-    def f5():
-        return lst[4]()
-    lst = [None,f1,f2,f3,f4,f5]
-
-    def f(n):
-        if lst[5]():
-            return n
-        return f(n)+1
-
-    def fn():
-        return f(0)
-    data = wrap_stackless_function(fn)
-    assert int(data.strip()) > 500
-
-
-
-def wrap_stackless_function(fn):
-    def entry_point(argv):
-        os.write(1, str(fn())+"\n")
-        return 0
-
-    t = Translator(entry_point)
-    s_list_of_strings = SomeList(ListDef(None, SomeString()))
-    ann = t.annotate([s_list_of_strings])
-    t.specialize()
-    cbuilder = t.cbuilder(standalone=True)
-    cbuilder.stackless = True
-    cbuilder.generate_source()
-    cbuilder.compile()
-    return cbuilder.cmdexec('')
-
-def test_stack_unwind():
-    def f():
-        stack_unwind()
-        return 42
-
-    data = wrap_stackless_function(f)
-    assert int(data.strip()) == 42
-
-def test_auto_stack_unwind():
-    def f(n):
-        if n == 1:
-            return 1
-        return (n+f(n-1)) % 1291
-
-    def fn():
-        return f(10**6)
-    data = wrap_stackless_function(fn)
-    assert int(data.strip()) == 704

Modified: pypy/dist/pypy/translator/transform.py
==============================================================================
--- pypy/dist/pypy/translator/transform.py	(original)
+++ pypy/dist/pypy/translator/transform.py	Tue Oct 25 13:32:15 2005
@@ -14,7 +14,7 @@
 from pypy.translator.annrpython import CannotSimplify
 from pypy.annotation import model as annmodel
 from pypy.annotation.specialize import MemoTable
-from pypy.rpython.objectmodel import stack_check
+from pypy.rpython.rstack import stack_check
 
 def checkgraphs(self, blocks):
     seen = {}



More information about the Pypy-commit mailing list