[pypy-commit] pypy missing-tp_new: merge default into branch

mattip pypy.commits at gmail.com
Fri Nov 18 08:25:45 EST 2016


Author: Matti Picus <matti.picus at gmail.com>
Branch: missing-tp_new
Changeset: r88460:502ffda2620c
Date: 2016-11-18 08:01 +0200
http://bitbucket.org/pypy/pypy/changeset/502ffda2620c/

Log:	merge default into branch

diff --git a/pypy/doc/whatsnew-head.rst b/pypy/doc/whatsnew-head.rst
--- a/pypy/doc/whatsnew-head.rst
+++ b/pypy/doc/whatsnew-head.rst
@@ -14,3 +14,9 @@
 .. branch: union-side-effects-2
 
 Try to improve the consistency of RPython annotation unions.
+
+.. branch: pytest-2.9.2
+
+.. branch: clean-exported-state
+
+Clean-ups in the jit optimizeopt
diff --git a/pypy/module/_ssl/test/test_ssl.py b/pypy/module/_ssl/test/test_ssl.py
--- a/pypy/module/_ssl/test/test_ssl.py
+++ b/pypy/module/_ssl/test/test_ssl.py
@@ -182,6 +182,12 @@
             return s
             """)
 
+    def teardown_method(self, method):
+        # pytest may keep some objects alive.
+        # So do some clean-up now without waiting for them to die
+        from ..interp_ssl import SOCKET_STORAGE
+        SOCKET_STORAGE._dict.clear()
+
     def test_connect(self):
         import socket, gc
         ss = socket.ssl(self.s)
@@ -484,7 +490,7 @@
             c = _socket.socket()
             c.connect(s.getsockname())
             c.setblocking(False)
-            
+
             c = ctx._wrap_socket(c, False)
             try:
                 exc = raises(_ssl.SSLWantReadError, c.do_handshake)
diff --git a/pypy/module/cpyext/slotdefs.py b/pypy/module/cpyext/slotdefs.py
--- a/pypy/module/cpyext/slotdefs.py
+++ b/pypy/module/cpyext/slotdefs.py
@@ -387,7 +387,10 @@
         size = pybuf.c_len
         ndim = widen(pybuf.c_ndim)
         shape =   [pybuf.c_shape[i]   for i in range(ndim)]
-        strides = [pybuf.c_strides[i] for i in range(ndim)]
+        if pybuf.c_strides:
+            strides = [pybuf.c_strides[i] for i in range(ndim)]
+        else:
+            strides = [1]
         if pybuf.c_format:
             format = rffi.charp2str(pybuf.c_format)
         else:
diff --git a/pytest.py b/pytest.py
--- a/pytest.py
+++ b/pytest.py
@@ -1,3 +1,4 @@
+#!/usr/bin/env python
 # PYTHON_ARGCOMPLETE_OK
 """
 pytest: unit and functional testing with Python.
diff --git a/rpython/jit/metainterp/compile.py b/rpython/jit/metainterp/compile.py
--- a/rpython/jit/metainterp/compile.py
+++ b/rpython/jit/metainterp/compile.py
@@ -108,7 +108,7 @@
     """
     log_noopt = False
 
-    def __init__(self, trace, celltoken, state,
+    def __init__(self, trace, celltoken, state, runtime_boxes,
                  call_pure_results=None, enable_opts=None,
                  inline_short_preamble=True):
         self.trace = trace
@@ -117,6 +117,8 @@
         self.state = state
         self.call_pure_results = call_pure_results
         self.inline_short_preamble = inline_short_preamble
+        assert runtime_boxes is not None
+        self.runtime_boxes = runtime_boxes
 
     def optimize(self, metainterp_sd, jitdriver_sd, optimizations, unroll):
         from rpython.jit.metainterp.optimizeopt.unroll import UnrollOptimizer
@@ -124,7 +126,11 @@
         assert unroll # we should not be here if it's disabled
         opt = UnrollOptimizer(metainterp_sd, jitdriver_sd, optimizations)
         return opt.optimize_peeled_loop(self.trace, self.celltoken, self.state,
-            self.call_pure_results, self.inline_short_preamble)
+            self.runtime_boxes, self.call_pure_results, self.inline_short_preamble)
+
+    def forget_optimization_info(self):
+        self.state.forget_optimization_info()
+        CompileData.forget_optimization_info(self)
 
 def show_procedures(metainterp_sd, procedure=None, error=None):
     # debugging
@@ -290,7 +296,7 @@
     start_descr = TargetToken(jitcell_token,
                               original_jitcell_token=jitcell_token)
     jitcell_token.target_tokens = [start_descr]
-    loop_data = UnrolledLoopData(trace, jitcell_token, start_state,
+    loop_data = UnrolledLoopData(trace, jitcell_token, start_state, jumpargs,
                                  call_pure_results=call_pure_results,
                                  enable_opts=enable_opts)
     try:
@@ -360,7 +366,7 @@
     history.record(rop.JUMP, jumpargs[:], None, descr=loop_jitcell_token)
     enable_opts = jitdriver_sd.warmstate.enable_opts
     call_pure_results = metainterp.call_pure_results
-    loop_data = UnrolledLoopData(trace, loop_jitcell_token, start_state,
+    loop_data = UnrolledLoopData(trace, loop_jitcell_token, start_state, jumpargs,
                                  call_pure_results=call_pure_results,
                                  enable_opts=enable_opts)
     try:
@@ -372,6 +378,7 @@
         history.cut(cut)
         history.record(rop.JUMP, jumpargs[:], None, descr=loop_jitcell_token)
         loop_data = UnrolledLoopData(trace, loop_jitcell_token, start_state,
+                                     jumpargs,
                                      call_pure_results=call_pure_results,
                                      enable_opts=enable_opts,
                                      inline_short_preamble=False)
@@ -516,7 +523,7 @@
     for item in lst:
         item.set_forwarded(None)
         # XXX we should really do it, but we need to remember the values
-        #     somehoe for ContinueRunningNormally
+        #     somehow for ContinueRunningNormally
         if reset_values:
             item.reset_value()
 
diff --git a/rpython/jit/metainterp/optimizeopt/heap.py b/rpython/jit/metainterp/optimizeopt/heap.py
--- a/rpython/jit/metainterp/optimizeopt/heap.py
+++ b/rpython/jit/metainterp/optimizeopt/heap.py
@@ -12,7 +12,7 @@
 from rpython.jit.metainterp.optimize import InvalidLoop
 from rpython.jit.metainterp.resoperation import rop, ResOperation, OpHelpers,\
      AbstractResOp, GuardResOp
-from rpython.rlib.objectmodel import we_are_translated
+from rpython.rlib.objectmodel import we_are_translated, we_are_debug
 from rpython.jit.metainterp.optimizeopt import info
         
 
@@ -172,7 +172,7 @@
 
     def _getfield(self, opinfo, descr, optheap, true_force=True):
         res = opinfo.getfield(descr, optheap)
-        if not we_are_translated() and res:
+        if we_are_debug() and res:
             if isinstance(opinfo, info.AbstractStructPtrInfo):
                 assert opinfo in self.cached_infos
         if isinstance(res, PreambleOp):
@@ -202,7 +202,7 @@
 
     def _getfield(self, opinfo, descr, optheap, true_force=True):
         res = opinfo.getitem(descr, self.index, optheap)
-        if not we_are_translated() and res:
+        if we_are_debug() and res:
             if isinstance(opinfo, info.ArrayPtrInfo):
                 assert opinfo in self.cached_infos
         if (isinstance(res, PreambleOp) and
diff --git a/rpython/jit/metainterp/optimizeopt/optimizer.py b/rpython/jit/metainterp/optimizeopt/optimizer.py
--- a/rpython/jit/metainterp/optimizeopt/optimizer.py
+++ b/rpython/jit/metainterp/optimizeopt/optimizer.py
@@ -24,9 +24,20 @@
 llhelper.CONST_NULLREF = llhelper.CONST_NULL
 REMOVED = AbstractResOp()
 
+def check_no_forwarding(lsts):
+    for lst in lsts:
+        for op in lst:
+            assert op.get_forwarded() is None
+
 class LoopInfo(object):
     label_op = None
 
+    def _check_no_forwarding(self):
+        pass
+
+    def forget_optimization_info(self):
+        pass
+
 class BasicLoopInfo(LoopInfo):
     def __init__(self, inputargs, quasi_immutable_deps, jump_op):
         self.inputargs = inputargs
@@ -555,7 +566,8 @@
         return (BasicLoopInfo(trace.inputargs, self.quasi_immutable_deps, last_op),
                 self._newoperations)
 
-    def _clean_optimization_info(self, lst):
+    @staticmethod
+    def _clean_optimization_info(lst):
         for op in lst:
             if op.get_forwarded() is not None:
                 op.set_forwarded(None)
diff --git a/rpython/jit/metainterp/optimizeopt/shortpreamble.py b/rpython/jit/metainterp/optimizeopt/shortpreamble.py
--- a/rpython/jit/metainterp/optimizeopt/shortpreamble.py
+++ b/rpython/jit/metainterp/optimizeopt/shortpreamble.py
@@ -5,6 +5,7 @@
      rop, AbstractResOp, AbstractInputArg
 from rpython.jit.metainterp.history import Const, make_hashable_int,\
      TreeLoop
+from rpython.jit.metainterp.optimize import InvalidLoop
 from rpython.jit.metainterp.optimizeopt import info
 
 class PreambleOp(AbstractResOp):
@@ -18,7 +19,7 @@
     See force_op_from_preamble for details how the extra things are put.
     """
     op = None
-    
+
     def __init__(self, op, preamble_op, invented_name):
         self.op = op
         self.preamble_op = preamble_op
@@ -51,7 +52,13 @@
 class AbstractShortOp(object):
     """ An operation that is potentially produced by the short preamble
     """
-    pass
+    res = None
+
+    def _check_no_forwarding(self):
+        assert self.res.get_forwarded() is None
+
+    def forget_optimization_info(self):
+        self.res.clear_forwarded()
 
 class HeapOp(AbstractShortOp):
     def __init__(self, res, getfield_op):
@@ -101,6 +108,14 @@
                                        descr=sop.getdescr())
         return ProducedShortOp(self, preamble_op)
 
+    def _check_no_forwarding(self):
+        AbstractShortOp._check_no_forwarding(self)
+        assert self.getfield_op.get_forwarded() is None
+
+    def forget_optimization_info(self):
+        AbstractShortOp.forget_optimization_info(self)
+        self.getfield_op.clear_forwarded()
+
     def __repr__(self):
         return "HeapOp(%r)" % (self.res,)
 
@@ -193,6 +208,16 @@
                 l.append(pop)
         return l
 
+    def _check_no_forwarding(self):
+        AbstractShortOp._check_no_forwarding(self)
+        self.one._check_no_forwarding()
+        self.two._check_no_forwarding()
+
+    def forget_optimization_info(self):
+        AbstractShortOp.forget_optimization_info(self)
+        self.one.forget_optimization_info()
+        self.two.forget_optimization_info()
+
     def repr(self, memo):
         return "CompoundOp(%s, %s, %s)" % (self.res.repr(memo),
                                            self.one.repr(memo),
@@ -203,7 +228,7 @@
 
 class ProducedShortOp(AbstractProducedShortOp):
     invented_name = False
-    
+
     def __init__(self, short_op, preamble_op):
         self.short_op = short_op
         self.preamble_op = preamble_op
@@ -215,6 +240,14 @@
     def repr(self, memo):
         return self.short_op.repr(memo)
 
+    def _check_no_forwarding(self):
+        self.short_op._check_no_forwarding()
+        assert self.preamble_op.get_forwarded() is None
+
+    def forget_optimization_info(self):
+        self.short_op.forget_optimization_info()
+        self.preamble_op.clear_forwarded()
+
     def __repr__(self):
         return "%r -> %r" % (self.short_op, self.preamble_op)
 
@@ -235,6 +268,14 @@
     def repr(self, memo):
         return "INP(%s)" % (self.res.repr(memo),)
 
+    def _check_no_forwarding(self):
+        AbstractShortOp._check_no_forwarding(self)
+        assert self.preamble_op.get_forwarded() is None
+
+    def forget_optimization_info(self):
+        AbstractShortOp.forget_optimization_info(self)
+        self.preamble_op.clear_forwarded()
+
     def __repr__(self):
         return "INP(%r -> %r)" % (self.res, self.preamble_op)
 
@@ -454,16 +495,23 @@
         self.sb = sb
         self.extra_same_as = self.sb.extra_same_as
         self.target_token = target_token
+        self.build_inplace = False
 
     def setup(self, jump_args, short, label_args):
         self.jump_args = jump_args
         self.short = short
         self.label_args = label_args
+        self.build_inplace = True
 
     def add_preamble_op(self, preamble_op):
         """ Notice that we're actually using the preamble_op, add it to
         label and jump
         """
+        # Could this be considered a speculative error?
+        # This check should only fail when trying to jump to an existing trace
+        # by forcing portions of the virtualstate.
+        if not self.build_inplace:
+            raise InvalidLoop("Forcing boxes would modify an existing short preamble")
         op = preamble_op.op.get_box_replacement()
         if preamble_op.invented_name:
             self.extra_same_as.append(op)
diff --git a/rpython/jit/metainterp/optimizeopt/test/test_util.py b/rpython/jit/metainterp/optimizeopt/test/test_util.py
--- a/rpython/jit/metainterp/optimizeopt/test/test_util.py
+++ b/rpython/jit/metainterp/optimizeopt/test/test_util.py
@@ -573,6 +573,7 @@
         #
         compile_data.enable_opts = self.enable_opts
         state = optimize_trace(metainterp_sd, None, compile_data)
+        state[0]._check_no_forwarding()
         return state
 
     def _convert_call_pure_results(self, d):
@@ -621,7 +622,7 @@
         start_state, preamble_ops = self._do_optimize_loop(preamble_data)
         preamble_data.forget_optimization_info()
         loop_data = compile.UnrolledLoopData(preamble_data.trace,
-            celltoken, start_state, call_pure_results)
+            celltoken, start_state, runtime_boxes, call_pure_results)
         loop_info, ops = self._do_optimize_loop(loop_data)
         preamble = TreeLoop('preamble')
         preamble.inputargs = start_state.renamed_inputargs
diff --git a/rpython/jit/metainterp/optimizeopt/unroll.py b/rpython/jit/metainterp/optimizeopt/unroll.py
--- a/rpython/jit/metainterp/optimizeopt/unroll.py
+++ b/rpython/jit/metainterp/optimizeopt/unroll.py
@@ -6,7 +6,7 @@
 from rpython.jit.metainterp.optimizeopt import info, intutils
 from rpython.jit.metainterp.optimize import InvalidLoop, SpeculativeError
 from rpython.jit.metainterp.optimizeopt.optimizer import Optimizer,\
-     Optimization, LoopInfo, MININT, MAXINT, BasicLoopInfo
+     Optimization, LoopInfo, MININT, MAXINT, BasicLoopInfo, check_no_forwarding
 from rpython.jit.metainterp.optimizeopt.vstring import StrPtrInfo
 from rpython.jit.metainterp.optimizeopt.virtualstate import (
     VirtualStateConstructor, VirtualStatesCantMatch)
@@ -35,7 +35,7 @@
 
     def setinfo_from_preamble_list(self, lst, infos):
         for item in lst:
-            if item is None:
+            if item is None or isinstance(item, Const):
                 continue
             i = infos.get(item, None)
             if i is not None:
@@ -97,7 +97,6 @@
         elif isinstance(preamble_info, info.FloatConstInfo):
             op.set_forwarded(preamble_info._const)
 
-
 class UnrollOptimizer(Optimization):
     """Unroll the loop into two iterations. The first one will
     become the preamble or entry bridge (don't think there is a
@@ -115,26 +114,22 @@
         return modifier.get_virtual_state(args)
 
     def _check_no_forwarding(self, lsts, check_newops=True):
-        for lst in lsts:
-            for op in lst:
-                assert op.get_forwarded() is None
+        check_no_forwarding(lsts)
         if check_newops:
             assert not self.optimizer._newoperations
 
-
     def optimize_preamble(self, trace, runtime_boxes, call_pure_results, memo):
         info, newops = self.optimizer.propagate_all_forward(
             trace.get_iter(), call_pure_results, flush=False)
         exported_state = self.export_state(info.jump_op.getarglist(),
-                                           info.inputargs,
-                                           runtime_boxes, memo)
+                                           info.inputargs, memo)
         exported_state.quasi_immutable_deps = info.quasi_immutable_deps
         # we need to absolutely make sure that we've cleaned up all
         # the optimization info
         self.optimizer._clean_optimization_info(self.optimizer._newoperations)
         return exported_state, self.optimizer._newoperations
 
-    def optimize_peeled_loop(self, trace, celltoken, state,
+    def optimize_peeled_loop(self, trace, celltoken, state, runtime_boxes,
                              call_pure_results, inline_short_preamble=True):
         trace = trace.get_iter()
         try:
@@ -186,7 +181,7 @@
 
         try:
             new_virtual_state = self.jump_to_existing_trace(
-                    end_jump, label_op, state.runtime_boxes, force_boxes=False)
+                    end_jump, label_op, runtime_boxes, force_boxes=False)
         except InvalidLoop:
             # inlining short preamble failed, jump to preamble
             self.jump_to_preamble(celltoken, end_jump, info)
@@ -199,7 +194,7 @@
             # to the preamble.
             try:
                 new_virtual_state = self.jump_to_existing_trace(
-                        end_jump, label_op, state.runtime_boxes, force_boxes=True)
+                        end_jump, label_op, runtime_boxes, force_boxes=True)
             except InvalidLoop:
                 pass
 
@@ -276,8 +271,7 @@
             debug_print("Retrace count reached, jumping to preamble")
             return self.jump_to_preamble(cell_token, jump_op, info)
         exported_state = self.export_state(info.jump_op.getarglist(),
-                                           info.inputargs, runtime_boxes,
-                                           box_names_memo)
+                                           info.inputargs, box_names_memo)
         exported_state.quasi_immutable_deps = self.optimizer.quasi_immutable_deps
         self.optimizer._clean_optimization_info(self.optimizer._newoperations)
         return exported_state, self.optimizer._newoperations
@@ -440,8 +434,7 @@
                 continue
             self._expand_info(item, infos)
 
-    def export_state(self, original_label_args, renamed_inputargs,
-                     runtime_boxes, memo):
+    def export_state(self, original_label_args, renamed_inputargs, memo):
         end_args = [self.optimizer.force_box_for_end_of_preamble(a)
                     for a in original_label_args]
         self.optimizer.flush()
@@ -462,16 +455,17 @@
             op = produced_op.short_op.res
             if not isinstance(op, Const):
                 self._expand_info(op, infos)
-        self.optimizer._clean_optimization_info(end_args)
         return ExportedState(label_args, end_args, virtual_state, infos,
                              short_boxes, renamed_inputargs,
-                             short_inputargs, runtime_boxes, memo)
+                             short_inputargs, memo)
 
     def import_state(self, targetargs, exported_state):
         # the mapping between input args (from old label) and what we need
         # to actually emit. Update the info
         assert (len(exported_state.next_iteration_args) ==
                 len(targetargs))
+        self._check_no_forwarding([targetargs])
+        exported_state._check_no_forwarding()
         for i, target in enumerate(exported_state.next_iteration_args):
             source = targetargs[i]
             assert source is not target
@@ -527,13 +521,11 @@
     * renamed_inputargs - the start label arguments in optimized version
     * short_inputargs - the renamed inputargs for short preamble
     * quasi_immutable_deps - for tracking quasi immutables
-    * runtime_boxes - runtime values for boxes, necessary when generating
-                      guards to jump to
     """
 
     def __init__(self, end_args, next_iteration_args, virtual_state,
                  exported_infos, short_boxes, renamed_inputargs,
-                 short_inputargs, runtime_boxes, memo):
+                 short_inputargs, memo):
         self.end_args = end_args
         self.next_iteration_args = next_iteration_args
         self.virtual_state = virtual_state
@@ -541,8 +533,8 @@
         self.short_boxes = short_boxes
         self.renamed_inputargs = renamed_inputargs
         self.short_inputargs = short_inputargs
-        self.runtime_boxes = runtime_boxes
         self.dump(memo)
+        self.forget_optimization_info()
 
     def dump(self, memo):
         if have_debug_prints():
@@ -552,5 +544,35 @@
                 debug_print("  " + box.repr(memo))
             debug_stop("jit-log-exported-state")
 
+    def _check_no_forwarding(self):
+        """ Ensures that no optimization state is attached to relevant operations
+        before importing anything. """
+        # Some of these may be redunant
+        check_no_forwarding([
+            self.end_args,
+            self.next_iteration_args,
+            self.renamed_inputargs,
+            self.short_inputargs,
+            self.exported_infos.keys()])
+        for box in self.short_boxes:
+            box._check_no_forwarding()
+
+    def forget_optimization_info(self):
+        """ Clean up optimization info on all operations stored in the ExportedState.
+
+        This function needs to be called when exporting the optimizer state to
+        prevent leaking of optimization information between invocations of the
+        optimizer.
+
+        That includes cleaning up in the event that optimize_peeled_loop() fails
+        with an InvalidLoop exception, as optimize_peeled_loop() mutates the
+        contents of ExportedState.
+        """
+        Optimizer._clean_optimization_info(self.renamed_inputargs)
+        for box in self.exported_infos.iterkeys():
+            box.clear_forwarded()
+        for box in self.short_boxes:
+            box.forget_optimization_info()
+
     def final(self):
         return False
diff --git a/rpython/jit/metainterp/pyjitpl.py b/rpython/jit/metainterp/pyjitpl.py
--- a/rpython/jit/metainterp/pyjitpl.py
+++ b/rpython/jit/metainterp/pyjitpl.py
@@ -1976,6 +1976,8 @@
         self.aborted_tracing_greenkey = None
 
     def retrace_needed(self, trace, exported_state):
+        if not we_are_translated():
+            exported_state._check_no_forwarding()
         self.partial_trace = trace
         self.retracing_from = self.potential_retrace_position
         self.exported_state = exported_state
diff --git a/rpython/jit/metainterp/resoperation.py b/rpython/jit/metainterp/resoperation.py
--- a/rpython/jit/metainterp/resoperation.py
+++ b/rpython/jit/metainterp/resoperation.py
@@ -52,6 +52,10 @@
         llop.debug_print(lltype.Void, "setting forwarded on:", self.__class__.__name__)
         raise SettingForwardedOnAbstractValue()
 
+    def clear_forwarded(self):
+        if self.get_forwarded() is not None:
+            self.set_forwarded(None)
+
     @specialize.arg(1)
     def get_box_replacement(op, not_const=False):
         # Read the chain "op, op._forwarded, op._forwarded._forwarded..."
diff --git a/rpython/rlib/objectmodel.py b/rpython/rlib/objectmodel.py
--- a/rpython/rlib/objectmodel.py
+++ b/rpython/rlib/objectmodel.py
@@ -295,10 +295,15 @@
 
 malloc_zero_filled = CDefinedIntSymbolic('MALLOC_ZERO_FILLED', default=0)
 _translated_to_c = CDefinedIntSymbolic('1 /*_translated_to_c*/', default=0)
+_rpy_assert_value = CDefinedIntSymbolic('RPY_ASSERT_VALUE', default=1)
 
 def we_are_translated_to_c():
     return we_are_translated() and _translated_to_c
 
+def we_are_debug():
+    """ Returns True when not translated or translated with debugging enabled. """
+    return not we_are_translated() or (_translated_to_c and _rpy_assert_value)
+
 # ____________________________________________________________
 
 def instantiate(cls, nonmovable=False):
diff --git a/rpython/tool/algo/graphlib.py b/rpython/tool/algo/graphlib.py
--- a/rpython/tool/algo/graphlib.py
+++ b/rpython/tool/algo/graphlib.py
@@ -25,18 +25,27 @@
     return edges
 
 def depth_first_search(root, vertices, edges):
-    seen = {}
+    seen = set([root])
     result = []
-    def visit(vertex):
-        result.append(('start', vertex))
-        seen[vertex] = True
-        for edge in edges[vertex]:
-            w = edge.target
-            if w in vertices and w not in seen:
-                visit(w)
-        result.append(('stop', vertex))
-    visit(root)
-    return result
+    stack = []
+    while True:
+        result.append(('start', root))
+        stack.append((root, iter(edges[root])))
+        while True:
+            vertex, iterator = stack[-1]
+            try:
+                edge = next(iterator)
+            except StopIteration:
+                stack.pop()
+                result.append(('stop', vertex))
+                if not stack:
+                    return result
+            else:
+                w = edge.target
+                if w in vertices and w not in seen:
+                    seen.add(w)
+                    root = w
+                    break
 
 def vertices_reachable_from(root, vertices, edges):
     for event, v in depth_first_search(root, vertices, edges):
@@ -97,13 +106,20 @@
             for edge in edges[v]:
                 if edge.target in vertices:
                     edgestack.append(edge)
-                    visit(edge.target)
+                    yield visit(edge.target)
                     edgestack.pop()
             stackpos[v] = None
         else:
             if stackpos[v] is not None:   # back-edge
                 result.append(edgestack[stackpos[v]:])
-    visit(root)
+
+    pending = [visit(root)]
+    while pending:
+        generator = pending[-1]
+        try:
+            pending.append(next(generator))
+        except StopIteration:
+            pending.pop()
     return result        
 
 
@@ -164,14 +180,20 @@
                 raise CycleFound
             if w in unvisited:
                 del unvisited[w]
-                visit(w)
+                yield visit(w)
         del visiting[vertex]
     try:
         unvisited = vertices.copy()
         while unvisited:
             visiting = {}
             root = unvisited.popitem()[0]
-            visit(root)
+            pending = [visit(root)]
+            while pending:
+                generator = pending[-1]
+                try:
+                    pending.append(next(generator))
+                except StopIteration:
+                    pending.pop()
     except CycleFound:
         return False
     else:
diff --git a/rpython/tool/algo/test/test_graphlib.py b/rpython/tool/algo/test/test_graphlib.py
--- a/rpython/tool/algo/test/test_graphlib.py
+++ b/rpython/tool/algo/test/test_graphlib.py
@@ -20,6 +20,22 @@
         'G': [],
         }
 
+    def test_depth_first_search(self):
+        # 'D' missing from the list of vertices
+        lst = depth_first_search('A', list('ABCEFG'), self.edges)
+        assert lst == [
+            ('start', 'A'),
+            ('start', 'B'),
+            ('start', 'E'),
+            ('start', 'C'),
+            ('start', 'F'),
+            ('stop', 'F'),
+            ('stop', 'C'),
+            ('stop', 'E'),
+            ('stop', 'B'),
+            ('stop', 'A'),
+        ]
+
     def test_strong_components(self):
         edges = self.edges
         saved = copy_edges(edges)
diff --git a/rpython/translator/c/src/support.h b/rpython/translator/c/src/support.h
--- a/rpython/translator/c/src/support.h
+++ b/rpython/translator/c/src/support.h
@@ -31,8 +31,10 @@
 RPY_EXTERN
 void RPyAssertFailed(const char* filename, long lineno,
                      const char* function, const char *msg);
+#  define RPY_ASSERT_VALUE 1
 #else
 #  define RPyAssert(x, msg)   /* nothing */
+#  define RPY_ASSERT_VALUE 0
 #endif
 
 RPY_EXTERN


More information about the pypy-commit mailing list