[pypy-svn] r79148 - in pypy/branch/jit-free/pypy/jit: backend backend/llgraph metainterp metainterp/test

arigo at codespeak.net arigo at codespeak.net
Tue Nov 16 15:49:37 CET 2010


Author: arigo
Date: Tue Nov 16 15:49:35 2010
New Revision: 79148

Modified:
   pypy/branch/jit-free/pypy/jit/backend/llgraph/llimpl.py
   pypy/branch/jit-free/pypy/jit/backend/llgraph/runner.py
   pypy/branch/jit-free/pypy/jit/backend/model.py
   pypy/branch/jit-free/pypy/jit/metainterp/compile.py
   pypy/branch/jit-free/pypy/jit/metainterp/history.py
   pypy/branch/jit-free/pypy/jit/metainterp/memmgr.py
   pypy/branch/jit-free/pypy/jit/metainterp/test/test_memmgr.py
Log:
(antocuni, arigo)

* Make even more sure that loops are freed.

* Make the llgraph backend complain as soon as we are
  trying to run a freed loop.

* Write a failing test for the memory manager.
  The fix is setting the _keepalive_target_looktokens.


Modified: pypy/branch/jit-free/pypy/jit/backend/llgraph/llimpl.py
==============================================================================
--- pypy/branch/jit-free/pypy/jit/backend/llgraph/llimpl.py	(original)
+++ pypy/branch/jit-free/pypy/jit/backend/llgraph/llimpl.py	Tue Nov 16 15:49:35 2010
@@ -161,6 +161,8 @@
 # ____________________________________________________________
 
 class CompiledLoop(object):
+    has_been_freed = False
+
     def __init__(self):
         self.inputargs = []
         self.operations = []
@@ -285,6 +287,11 @@
     del _variables[:]
     return _to_opaque(CompiledLoop())
 
+def mark_as_free(loop):
+    loop = _from_opaque(loop)
+    assert not loop.has_been_freed
+    loop.has_been_freed = True
+
 def compile_start_int_var(loop):
     return compile_start_ref_var(loop, lltype.Signed)
 
@@ -429,6 +436,7 @@
         verbose = True
         self.opindex = 0
         while True:
+            assert not self.loop.has_been_freed
             op = self.loop.operations[self.opindex]
             args = [self.getenv(v) for v in op.args]
             if not op.is_final():
@@ -1609,6 +1617,7 @@
 setannotation(compile_add_fail, annmodel.SomeInteger())
 setannotation(compile_add_fail_arg, annmodel.s_None)
 setannotation(compile_redirect_fail, annmodel.s_None)
+setannotation(mark_as_free, annmodel.s_None)
 
 setannotation(new_frame, s_Frame)
 setannotation(frame_clear, annmodel.s_None)

Modified: pypy/branch/jit-free/pypy/jit/backend/llgraph/runner.py
==============================================================================
--- pypy/branch/jit-free/pypy/jit/backend/llgraph/runner.py	(original)
+++ pypy/branch/jit-free/pypy/jit/backend/llgraph/runner.py	Tue Nov 16 15:49:35 2010
@@ -118,8 +118,10 @@
             self._descrs[key] = descr
             return descr
 
-    def compile_bridge(self, faildescr, inputargs, operations, log=True):
+    def compile_bridge(self, faildescr, inputargs, operations,
+                       original_loop_token, log=True):
         c = llimpl.compile_start()
+        original_loop_token._llgraph_loop_and_bridges.append(c)
         self._compile_loop_or_bridge(c, inputargs, operations)
         old, oldindex = faildescr._compiled_fail
         llimpl.compile_redirect_fail(old, oldindex, c)
@@ -131,9 +133,15 @@
         is not.
         """
         c = llimpl.compile_start()
+        assert not hasattr(loopdescr, '_llgraph_loop_and_bridges')
+        loopdescr._llgraph_loop_and_bridges = [c]
         loopdescr._llgraph_compiled_version = c
         self._compile_loop_or_bridge(c, inputargs, operations)
 
+    def free_loop_and_bridges(self, looptoken):
+        for c in looptoken._llgraph_loop_and_bridges:
+            llimpl.mark_as_free(c)
+
     def _compile_loop_or_bridge(self, c, inputargs, operations):
         var2index = {}
         for box in inputargs:

Modified: pypy/branch/jit-free/pypy/jit/backend/model.py
==============================================================================
--- pypy/branch/jit-free/pypy/jit/backend/model.py	(original)
+++ pypy/branch/jit-free/pypy/jit/backend/model.py	Tue Nov 16 15:49:35 2010
@@ -49,7 +49,8 @@
         """
         raise NotImplementedError
 
-    def compile_bridge(self, faildescr, inputargs, operations, log=True):
+    def compile_bridge(self, faildescr, inputargs, operations,
+                       original_loop_token, log=True):
         """Assemble the bridge.
         The FailDescr is the descr of the original guard that failed.
         """

Modified: pypy/branch/jit-free/pypy/jit/metainterp/compile.py
==============================================================================
--- pypy/branch/jit-free/pypy/jit/metainterp/compile.py	(original)
+++ pypy/branch/jit-free/pypy/jit/metainterp/compile.py	Tue Nov 16 15:49:35 2010
@@ -82,6 +82,7 @@
     send_loop_to_backend(metainterp_sd, loop, "loop")
     insert_loop_token(old_loop_tokens, loop_token)
     # mostly for tests: make sure we don't keep a reference to the LoopToken
+    metainterp.original_loop_token.record_loop_or_bridge(loop)
     loop.token = None
     metainterp.original_loop_token = None
     return loop_token
@@ -127,7 +128,8 @@
     if metainterp_sd.warmrunnerdesc is not None:    # for tests
         metainterp_sd.warmrunnerdesc.memory_manager.keep_loop_alive(loop.token)
 
-def send_bridge_to_backend(metainterp_sd, faildescr, inputargs, operations):
+def send_bridge_to_backend(metainterp_sd, faildescr, inputargs, operations,
+                           original_loop_token):
     n = metainterp_sd.cpu.get_fail_descr_number(faildescr)
     metainterp_sd.logger_ops.log_bridge(inputargs, operations, n)
     if not we_are_translated():
@@ -136,7 +138,8 @@
     metainterp_sd.profiler.start_backend()
     debug_start("jit-backend")
     try:
-        metainterp_sd.cpu.compile_bridge(faildescr, inputargs, operations)
+        metainterp_sd.cpu.compile_bridge(faildescr, inputargs, operations,
+                                         original_loop_token)
     finally:
         debug_stop("jit-backend")
     metainterp_sd.profiler.end_backend()
@@ -326,7 +329,8 @@
         if not we_are_translated():
             self._debug_suboperations = new_loop.operations
         send_bridge_to_backend(metainterp.staticdata, self, inputargs,
-                               new_loop.operations)
+                               new_loop.operations,
+                               metainterp.original_loop_token)
 
     def copy_all_attrbutes_into(self, res):
         # XXX a bit ugly to have to list them all here
@@ -510,10 +514,6 @@
         old_loop_tokens.append(new_loop_token)
         metainterp.set_compiled_merge_points(new_loop_token.outermost_greenkey,
                                              old_loop_tokens)
-        # mostly for tests: make sure we don't keep a reference
-        # to the LoopToken
-        new_loop.token = None
-        metainterp.original_loop_token = None
 
     def reset_counter_from_failure(self):
         pass
@@ -548,6 +548,11 @@
         # know exactly what we must do (ResumeGuardDescr/ResumeFromInterpDescr)
         prepare_last_operation(new_loop, target_loop_token)
         resumekey.compile_and_attach(metainterp, new_loop)
+        # mostly for tests: make sure we don't keep a reference
+        # to the LoopToken
+        metainterp.original_loop_token.record_loop_or_bridge(new_loop)
+        metainterp.original_loop_token = None
+        new_loop.token = None
     return target_loop_token
 
 def prepare_last_operation(new_loop, target_loop_token):

Modified: pypy/branch/jit-free/pypy/jit/metainterp/history.py
==============================================================================
--- pypy/branch/jit-free/pypy/jit/metainterp/history.py	(original)
+++ pypy/branch/jit-free/pypy/jit/metainterp/history.py	Tue Nov 16 15:49:35 2010
@@ -744,9 +744,18 @@
         # this loop or to a bridge attached to it.
         self.faildescr_indices = []
         # For memory management of assembled loops
-        self.contains_jumps_to = {}      # set of other LoopTokens
+        self._keepalive_target_looktokens = {}      # set of other LoopTokens
         self.generation = r_longlong(0)
 
+    def record_loop_or_bridge(self, loop):
+        # Records that the loop starting at the LoopToken 'self' ends up
+        # with 'loop', which may be either the loop itself or some pseudo-
+        # loop representing some bridge.
+        other_loop_token = loop.operations[-1].getdescr()
+        if isinstance(other_loop_token, LoopToken):
+            self._keepalive_target_looktokens[other_loop_token] = None
+            loop.operations[-1].setdescr(None)    # clear reference
+
     def __del__(self):
         for i in range(160):
             print '#',

Modified: pypy/branch/jit-free/pypy/jit/metainterp/memmgr.py
==============================================================================
--- pypy/branch/jit-free/pypy/jit/metainterp/memmgr.py	(original)
+++ pypy/branch/jit-free/pypy/jit/metainterp/memmgr.py	Tue Nov 16 15:49:35 2010
@@ -1,6 +1,7 @@
 import math
 from pypy.rlib.rarithmetic import r_longlong
 from pypy.rlib.debug import debug_start, debug_print, debug_stop
+from pypy.rlib.objectmodel import we_are_translated
 
 #
 # Logic to decide which loops are old and not used any more.
@@ -71,3 +72,6 @@
         debug_print("Loop tokens left:  ", newtotal)
         print self.alive_loops.keys()
         debug_stop("jit-free-memmgr")
+        if not we_are_translated():
+            from pypy.rlib import rgc
+            rgc.collect()

Modified: pypy/branch/jit-free/pypy/jit/metainterp/test/test_memmgr.py
==============================================================================
--- pypy/branch/jit-free/pypy/jit/metainterp/test/test_memmgr.py	(original)
+++ pypy/branch/jit-free/pypy/jit/metainterp/test/test_memmgr.py	Tue Nov 16 15:49:35 2010
@@ -80,6 +80,37 @@
         # we should see only the loop and the entry bridge
         self.check_tree_loop_count(2)
 
+    def test_target_loop_kept_alive(self):
+        myjitdriver = JitDriver(greens=['m'], reds=['n'])
+        def g(m):
+            n = 10
+            while n > 0:
+                myjitdriver.can_enter_jit(n=n, m=m)
+                myjitdriver.jit_merge_point(n=n, m=m)
+                n = n - 1
+            return 21
+        import gc
+        def f():
+            # create the loop and the entry bridge for 'g(5)'
+            for i in range(8):
+                g(5)
+            # create another loop and another entry bridge for 'g(7)',
+            # to increase the current_generation
+            for i in range(20):
+                g(7)
+                # reuse the existing loop and entry bridge for 'g(5)'.
+                # the generation of the entry bridge for g(5) should never
+                # grow too old.  The loop itself gets old, but is kept alive
+                # by the entry bridge via contains_jumps_to.
+                g(5)
+            return 42
+
+        res = self.meta_interp(f, [], loop_longevity=3)
+        assert res == 42
+
+        # we should see only the loop and the entry bridge for g(5) and g(7)
+        self.check_tree_loop_count(4)
+
 
     # we need another test that fails because we store
     # self._debug_suboperations in compile.py



More information about the Pypy-commit mailing list