[pypy-svn] r19303 - in pypy/dist/pypy/translator/c: . test

tismer at codespeak.net tismer at codespeak.net
Mon Oct 31 21:37:55 CET 2005


Author: tismer
Date: Mon Oct 31 21:37:54 2005
New Revision: 19303

Modified:
   pypy/dist/pypy/translator/c/genc.py
   pypy/dist/pypy/translator/c/stackless.py
   pypy/dist/pypy/translator/c/test/test_stackless.py
Log:
disabled graph optimization.
changed interface to stackless.

optimized stackless to do a full analysis which call-site really
needs stackless support. This almost halves the speed difference
between pypy-c-stackless and pypy-c.

This is most of what can be done. There is a small opportunity
to special-case tail recursive functions, but I doubt this
is much fruit.

Modified: pypy/dist/pypy/translator/c/genc.py
==============================================================================
--- pypy/dist/pypy/translator/c/genc.py	(original)
+++ pypy/dist/pypy/translator/c/genc.py	Mon Oct 31 21:37:54 2005
@@ -34,7 +34,7 @@
 
         if self.stackless:
             from pypy.translator.c.stackless import StacklessData
-            db.stacklessdata = StacklessData()
+            db.stacklessdata = StacklessData(db)
 
         # we need a concrete gcpolicy to do this
         self.libraries += db.gcpolicy.gc_libraries()
@@ -194,8 +194,8 @@
 
 # ____________________________________________________________
 
-#SPLIT_CRITERIA = 32767 # enable to support VC++ 6.0
 SPLIT_CRITERIA = 65535 # support VC++ 7.2
+#SPLIT_CRITERIA = 32767 # enable to support VC++ 6.0
 
 MARKER = '/*/*/' # provide an easy way to split after generating
 
@@ -225,9 +225,11 @@
         self.funcnodes = funcnodes
         self.othernodes = othernodes
         self.path = path
-        return # the below is under development
-        graph = CallTree(self.funcnodes, self.database)
-        graph.simulate()
+        # disabled this for a while, does worsen things
+#        graph = CallTree(self.funcnodes, self.database)
+#        graph.simulate()
+#        graph.optimize()
+#        self.funcnodes = graph.ordered_funcnodes()
 
     def uniquecname(self, name):
         assert name.endswith('.c')
@@ -273,7 +275,7 @@
         #
         # All declarations
         #
-        database= self.database
+        database = self.database
         structdeflist = database.getstructdeflist()
         name = 'structdef.h'
         fi = self.makefile(name)

Modified: pypy/dist/pypy/translator/c/stackless.py
==============================================================================
--- pypy/dist/pypy/translator/c/stackless.py	(original)
+++ pypy/dist/pypy/translator/c/stackless.py	Mon Oct 31 21:37:54 2005
@@ -13,26 +13,86 @@
 
 class StacklessData:
 
-    def __init__(self):
+    def __init__(self, database):
         self.frame_types = {}
         self.allsignatures = {}
         self.decode_table = []
+        self.stackless_roots = {}
 
         # start the decoding table with entries for the functions that
         # are written manually in ll_stackless.h
-        self.registerunwindable('LL_stackless_stack_unwind',
+        def reg(name):
+            self.stackless_roots[name.lower()] = True
+            return name
+
+        reg('LL_stack_unwind')
+        self.registerunwindable(reg('LL_stackless_stack_unwind'),
                                 lltype.FuncType([], lltype.Void),
                                 resume_points=1)
-        self.registerunwindable('LL_stackless_stack_frames_depth',
+        self.registerunwindable(reg('LL_stackless_stack_frames_depth'),
                                 lltype.FuncType([], lltype.Signed),
                                 resume_points=1)
-        self.registerunwindable('LL_stackless_switch',
+        self.registerunwindable(reg('LL_stackless_switch'),
                                 lltype.FuncType([Address], Address),
                                 resume_points=1)
-        self.registerunwindable('slp_end_of_yielding_function',
+        self.registerunwindable(reg('slp_end_of_yielding_function'),
                                 lltype.FuncType([], Address),
                                 resume_points=1)
 
+        self.can_reach_unwind = {}
+        self.database = database
+        self.count_calls = [0, 0]
+
+    def unwind_reachable(self, func):
+        reach_dict = self.can_reach_unwind
+        if func not in reach_dict:
+            self.setup()
+        return reach_dict[func]
+
+    def setup(self):
+        # to be called after database is complete, or we would
+        # not have valid externals info
+        self._compute_reach_unwind()
+
+    def _compute_reach_unwind(self):
+        callers = {}
+        assert self.database.completed
+        translator = self.database.translator
+        here = len(translator.functions)
+        for func in translator.functions:
+            callers[func] = []
+        for caller, callee in translator.complete_callgraph.values():
+            callers[caller].append(callee)
+        # add newly generated ones
+        for func in translator.functions[here:]:
+            callers.setdefault(func, [])
+        # check all callees if they can reach unwind
+        seen = self.can_reach_unwind
+        
+        pending = {}
+        ext = self.database.externalfuncs
+        def check_unwind(func):
+            if func in pending:
+                ret = func not in ext
+                # typical pseudo-recursion of externals
+                # but true recursions do unwind
+                seen[func] = ret
+                return ret
+            pending[func] = func
+            for callee in callers[func]:
+                if callee in seen:
+                    ret = seen[callee]
+                else:
+                    ret = check_unwind(callee)
+                if ret:
+                    break
+            else:
+                ret = func.__name__ in self.stackless_roots
+            del pending[func]
+            seen[func] = ret
+            return ret
+        [check_unwind(caller) for caller in callers if caller not in seen]
+
     def registerunwindable(self, functionname, FUNC, resume_points):
         if resume_points >= 1:
             try:
@@ -188,7 +248,22 @@
         del self.resumeblocks
 
     def check_directcall_result(self, op, err, specialreturnvalue=None):
-        stacklessdata = self.db.stacklessdata
+        slp = self.db.stacklessdata
+        # don't generate code for calls that cannot unwind
+        if not specialreturnvalue:
+            need_stackless = slp.unwind_reachable(self.graph.func)
+            if need_stackless:
+                try:
+                    callee = op.args[0].value._obj.graph.func
+                except AttributeError:
+                    pass # assume we need it really
+                else:
+                    need_stackless = slp.unwind_reachable(callee)
+            if not need_stackless:
+                slp.count_calls[False] += 1
+                return (super(SlpFunctionCodeGenerator, self)
+                        .check_directcall_result(op, err))
+        slp.count_calls[True] += 1
         block = self.currentblock
         curpos = block.operations.index(op)
         vars = list(variables_to_save_across_op(block, curpos))
@@ -206,7 +281,7 @@
                 variables_to_restore.append((v, '%s%d' % (
                     st.prefix, len(counts[st]))))
                 counts[st].append('(%s)%s' % (st.ctype, varname))
-        structname = stacklessdata.get_frame_type([len(counts[st]) for st in STATE_TYPES])
+        structname = slp.get_frame_type([len(counts[st]) for st in STATE_TYPES])
 
         # reorder the vars according to their type
         vars = sum([counts[st] for st in STATE_TYPES],[])
@@ -217,7 +292,7 @@
 
         # The globally unique number for our state
         # is the total number of saved states so far
-        globalstatecounter = len(stacklessdata.decode_table) + len(self.savelines)
+        globalstatecounter = len(slp.decode_table) + len(self.savelines)
         
         arguments = ['%d' % globalstatecounter] + vars
 

Modified: pypy/dist/pypy/translator/c/test/test_stackless.py
==============================================================================
--- pypy/dist/pypy/translator/c/test/test_stackless.py	(original)
+++ pypy/dist/pypy/translator/c/test/test_stackless.py	Mon Oct 31 21:37:54 2005
@@ -165,3 +165,28 @@
 
     data = wrap_stackless_function(f)
     assert int(data.strip()) == 1234567
+
+def test_yield_noswitch_frame():
+    # this time we make sure that function 'g' does not
+    # need to switch and even does not need to be stackless
+
+    def g(lst):
+        lst.append(2)
+        frametop_before_5 = yield_current_frame_to_caller()
+        lst.append(4)
+        return frametop_before_5
+
+    def f():
+        lst = [1]
+        frametop_before_4 = g(lst)
+        lst.append(3)
+        frametop_after_return = frametop_before_4.switch()
+        lst.append(5)
+        assert frametop_after_return is None
+        n = 0
+        for i in lst:
+            n = n*10 + i
+        return n
+
+    data = wrap_stackless_function(f)
+    assert int(data.strip()) == 12345



More information about the Pypy-commit mailing list