[pypy-svn] r78133 - in pypy/trunk/pypy/translator: . c c/src c/test

arigo at codespeak.net arigo at codespeak.net
Wed Oct 20 15:54:18 CEST 2010


Author: arigo
Date: Wed Oct 20 15:54:16 2010
New Revision: 78133

Modified:
   pypy/trunk/pypy/translator/c/funcgen.py
   pypy/trunk/pypy/translator/c/src/stack.h
   pypy/trunk/pypy/translator/c/test/test_genc.py
   pypy/trunk/pypy/translator/c/test/test_standalone.py
   pypy/trunk/pypy/translator/transform.py
Log:
(antocuni, arigo)

Hopefully fix lib-python/.../test_descr on 64-bit platforms.  The issue
is that we can get a cycle of tail recursions (more easily on x86-64
than on x86-32), and in such cycles, the stack overflow detection does
not work as a way to interrupt the cycle.  Thus we can get caught in
what seems like an infinite recursion (but is not in C).



Modified: pypy/trunk/pypy/translator/c/funcgen.py
==============================================================================
--- pypy/trunk/pypy/translator/c/funcgen.py	(original)
+++ pypy/trunk/pypy/translator/c/funcgen.py	Wed Oct 20 15:54:16 2010
@@ -427,7 +427,7 @@
         r = self.expr(op.result)
         return 'OP_CALL_ARGS((%s), %s);' % (', '.join(args), r)
 
-    def generic_call(self, FUNC, fnexpr, args_v, v_result):
+    def generic_call(self, FUNC, fnexpr, args_v, v_result, targets=None):
         args = []
         assert len(args_v) == len(FUNC.TO.ARGS)
         for v, ARGTYPE in zip(args_v, FUNC.TO.ARGS):
@@ -444,17 +444,23 @@
             # skip assignment of 'void' return value
             r = self.expr(v_result)
             line = '%s = %s' % (r, line)
+        if targets:
+            for func in targets:
+                graph = getattr(func._obj, 'graph', None)
+                if getattr(graph, 'inhibit_tail_call', False):
+                    line += '\nPYPY_INHIBIT_TAIL_CALL();'
+                    break
         return line
 
     def OP_DIRECT_CALL(self, op):
         fn = op.args[0]
         return self.generic_call(fn.concretetype, self.expr(fn),
-                                 op.args[1:], op.result)
+                                 op.args[1:], op.result, [fn.value])
 
     def OP_INDIRECT_CALL(self, op):
         fn = op.args[0]
         return self.generic_call(fn.concretetype, self.expr(fn),
-                                 op.args[1:-1], op.result)
+                                 op.args[1:-1], op.result, op.args[-1].value)
 
     def OP_ADR_CALL(self, op):
         ARGTYPES = [v.concretetype for v in op.args[1:]]

Modified: pypy/trunk/pypy/translator/c/src/stack.h
==============================================================================
--- pypy/trunk/pypy/translator/c/src/stack.h	(original)
+++ pypy/trunk/pypy/translator/c/src/stack.h	Wed Oct 20 15:54:16 2010
@@ -33,6 +33,12 @@
 		&& LL_stack_too_big_slowpath());
 }
 
+#ifdef __GNUC__
+#  define PYPY_INHIBIT_TAIL_CALL()   asm("/* inhibit_tail_call */")
+#else
+#  define PYPY_INHIBIT_TAIL_CALL()   /* add hints for other compilers here */
+#endif
+
 
 #ifndef PYPY_NOT_MAIN_FILE
 #include <stdio.h>

Modified: pypy/trunk/pypy/translator/c/test/test_genc.py
==============================================================================
--- pypy/trunk/pypy/translator/c/test/test_genc.py	(original)
+++ pypy/trunk/pypy/translator/c/test/test_genc.py	Wed Oct 20 15:54:16 2010
@@ -478,3 +478,23 @@
     t.bar = llhelper(FTPTR, a_f.make_func())
     fn = compile(chooser, [bool])
     assert fn(True)
+
+def test_inhibit_tail_call():
+    from pypy.rpython.lltypesystem import lltype
+    def foobar_fn(n):
+        return 42
+    foobar_fn._dont_inline_ = True
+    def main(n):
+        return foobar_fn(n)
+    #
+    t = Translation(main, [int], backend="c")
+    t.rtype()
+    t.context._graphof(foobar_fn).inhibit_tail_call = True
+    t.source_c()
+    lines = t.driver.cbuilder.c_source_filename.readlines()
+    for i, line in enumerate(lines):
+        if '= pypy_g_foobar_fn' in line:
+            break
+    else:
+        assert 0, "the call was not found in the C source"
+    assert 'PYPY_INHIBIT_TAIL_CALL();' in lines[i+1]

Modified: pypy/trunk/pypy/translator/c/test/test_standalone.py
==============================================================================
--- pypy/trunk/pypy/translator/c/test/test_standalone.py	(original)
+++ pypy/trunk/pypy/translator/c/test/test_standalone.py	Wed Oct 20 15:54:16 2010
@@ -16,11 +16,16 @@
 class StandaloneTests(object):
     config = None
 
-    def compile(self, entry_point, debug=True, shared=False):
+    def compile(self, entry_point, debug=True, shared=False,
+                stackcheck=False):
         t = TranslationContext(self.config)
         t.buildannotator().build_types(entry_point, [s_list_of_strings])
         t.buildrtyper().specialize()
 
+        if stackcheck:
+            from pypy.translator.transform import insert_ll_stackcheck
+            insert_ll_stackcheck(t)
+
         t.config.translation.shared = shared
 
         cbuilder = CStandaloneBuilder(t, entry_point, t.config)
@@ -630,6 +635,22 @@
             else:
                 os.environ['CC'] = old_cc
 
+    def test_inhibit_tail_call(self):
+        # the point is to check that the f()->f() recursion stops
+        from pypy.rlib.rstackovf import StackOverflow
+        def f(n):
+            if n <= 0:
+                return 42
+            return f(n+1)
+        def entry_point(argv):
+            try:
+                return f(1)
+            except StackOverflow:
+                print 'hi!'
+                return 0
+        t, cbuilder = self.compile(entry_point, stackcheck=True)
+        out = cbuilder.cmdexec("")
+        assert out.strip() == "hi!"
 
 class TestMaemo(TestStandalone):
     def setup_class(cls):

Modified: pypy/trunk/pypy/translator/transform.py
==============================================================================
--- pypy/trunk/pypy/translator/transform.py	(original)
+++ pypy/trunk/pypy/translator/transform.py	Wed Oct 20 15:54:16 2010
@@ -221,15 +221,19 @@
     stack_check_ptr_const = Constant(stack_check_ptr, lltype.typeOf(stack_check_ptr))
     edges = set()
     insert_in = set()
+    block2graph = {}
     for caller in translator.graphs:
         for block, callee in find_calls_from(translator, caller):
             if getattr(getattr(callee, 'func', None),
                        'insert_stack_check_here', False):
                 insert_in.add(callee.startblock)
+                block2graph[callee.startblock] = callee
                 continue
             if block is not caller.startblock:
                 edges.add((caller.startblock, block))
+                block2graph[caller.startblock] = caller
             edges.add((block, callee.startblock))
+            block2graph[block] = caller
 
     edgelist = [Edge(block1, block2) for (block1, block2) in edges]
     edgedict = make_edge_dict(edgelist)
@@ -241,6 +245,10 @@
         v.concretetype = lltype.Void
         unwind_op = SpaceOperation('direct_call', [stack_check_ptr_const], v)
         block.operations.insert(0, unwind_op)
+        # prevents cycles of tail calls from occurring -- such cycles would
+        # not consume any stack, so would turn into potentially infinite loops
+        graph = block2graph[block]
+        graph.inhibit_tail_call = True
     return len(insert_in)
 
 



More information about the Pypy-commit mailing list