[pypy-svn] r23383 - in pypy/dist/pypy: objspace/flow rpython/memory rpython/memory/test translator/c translator/tool

cfbolz at codespeak.net cfbolz at codespeak.net
Wed Feb 15 23:52:42 CET 2006


Author: cfbolz
Date: Wed Feb 15 23:52:39 2006
New Revision: 23383

Modified:
   pypy/dist/pypy/objspace/flow/model.py
   pypy/dist/pypy/rpython/memory/gctransform.py
   pypy/dist/pypy/rpython/memory/test/test_gctransform.py
   pypy/dist/pypy/translator/c/funcgen.py
   pypy/dist/pypy/translator/tool/make_dot.py
Log:
(arigo, cfbolz):

Started to work on the FrameworkGCTransformer.  It inserts matching
gc_push_roots/gc_pop_roots operations around calls and mallocs.

We had to extend the 'cleanup' model (again :-/); now an operation's cleanup is
a tuple (finally-operations, except-operations), where the former are always
run after the operation even if it raises, and the latter are run *only* if it
raises.

The Pygame viewer now displays these operations too.



Modified: pypy/dist/pypy/objspace/flow/model.py
==============================================================================
--- pypy/dist/pypy/objspace/flow/model.py	(original)
+++ pypy/dist/pypy/objspace/flow/model.py	Wed Feb 15 23:52:39 2006
@@ -191,10 +191,20 @@
             txt = "%s(%s)" % (txt, self.exitswitch)
         return txt
 
+    def reallyalloperations(self):
+        "Iterate over all operations, including cleanup sub-operations."
+        for op in self.operations:
+            yield op
+            cleanup = getattr(op, 'cleanup', None)
+            if cleanup is not None:
+                for lst in cleanup:
+                    for subop in lst:
+                        yield subop
+
     def getvariables(self):
         "Return all variables mentioned in this Block."
         result = self.inputargs[:]
-        for op in self.operations:
+        for op in self.reallyalloperations():
             result += op.args
             result.append(op.result)
         return uniqueitems([w for w in result if isinstance(w, Variable)])
@@ -202,7 +212,7 @@
     def getconstants(self):
         "Return all constants mentioned in this Block."
         result = self.inputargs[:]
-        for op in self.operations:
+        for op in self.reallyalloperations():
             result += op.args
         return uniqueitems([w for w in result if isinstance(w, Constant)])
 
@@ -210,7 +220,7 @@
         for a in mapping:
             assert isinstance(a, Variable), a
         self.inputargs = [mapping.get(a, a) for a in self.inputargs]
-        for op in self.operations:
+        for op in self.reallyalloperations():
             op.args = [mapping.get(a, a) for a in op.args]
             op.result = mapping.get(op.result, op.result)
         self.exitswitch = mapping.get(self.exitswitch, self.exitswitch)
@@ -334,6 +344,9 @@
         self.result = result      # either Variable or Constant instance
         self.offset = offset      # offset in code string
         if cleanup is not NOCLEANUP:
+            # None or a pair of tuples of operations:
+            # 0. operations to perform in all cases after 'self'
+            # 1. more operations to perform only in case of exception
             self.cleanup = cleanup
 
     def __eq__(self, other):
@@ -349,7 +362,17 @@
         return hash((self.opname,tuple(self.args),self.result))
 
     def __repr__(self):
-        return "%r = %s(%s)" % (self.result, self.opname, ", ".join(map(repr, self.args)))
+        s = "%r = %s(%s)" % (self.result, self.opname, ", ".join(map(repr, self.args)))
+        cleanup = getattr(self, 'cleanup', None)
+        if cleanup is not None:
+            lines = [s]
+            for case, lst in zip(("  finally: ", "   except: "), cleanup):
+                indent = case
+                for subop in lst:
+                    lines.append(indent + repr(subop))
+                    indent = ' ' * len(indent)
+            s = '\n'.join(lines)
+        return s
 
     def __reduce_ex__(self, *args):
         # avoid lots of useless list entities

Modified: pypy/dist/pypy/rpython/memory/gctransform.py
==============================================================================
--- pypy/dist/pypy/rpython/memory/gctransform.py	(original)
+++ pypy/dist/pypy/rpython/memory/gctransform.py	Wed Feb 15 23:52:39 2006
@@ -100,7 +100,9 @@
                 if var_needsgc(var):
                     newops.extend(self.push_alive(var))
         for op in block.operations:
-            newops.extend(self.replacement_operations(op))
+            ops, cleanup_before_exception = self.replacement_operations(op, livevars)
+            newops.extend(ops)
+            op = ops[-1]
             # XXX for now we assume that everything can raise
             if 1 or op.opname in EXCEPTION_RAISING_OPS:
                 if tuple(livevars) in livevars2cleanup:
@@ -111,7 +113,7 @@
                         cleanup_on_exception.extend(self.pop_alive(var))
                     cleanup_on_exception = tuple(cleanup_on_exception)
                     livevars2cleanup[tuple(livevars)] = cleanup_on_exception
-                op.cleanup = cleanup_on_exception
+                op.cleanup = tuple(cleanup_before_exception), cleanup_on_exception
             if var_needsgc(op.result):
                 if op.opname not in ('direct_call', 'indirect_call') and not var_ispyobj(op.result):
                     newops.extend(self.push_alive(op.result))
@@ -154,12 +156,12 @@
         if newops:
             block.operations = newops
 
-    def replacement_operations(self, op):
+    def replacement_operations(self, op, livevars):
         m = getattr(self, 'replace_' + op.opname, None)
         if m:
-            return m(op)
+            return m(op, livevars)
         else:
-            return [op]
+            return [op], []
 
 
     def push_alive(self, var):
@@ -342,29 +344,27 @@
                                      varoftype(lltype.Void), cleanup=None))
         return result
 
-    def replace_setfield(self, op):
+    def replace_setfield(self, op, livevars):
         if not var_needsgc(op.args[2]):
-            return [op]
+            return [op], []
         oldval = varoftype(op.args[2].concretetype)
         getoldvalop = SpaceOperation("getfield",
                                      [op.args[0], op.args[1]], oldval)
         result = [getoldvalop]
         result.extend(self.push_alive(op.args[2]))
         result.append(op)
-        result.extend(self.pop_alive(oldval))
-        return result
+        return result, self.pop_alive(oldval)
 
-    def replace_setarrayitem(self, op):
+    def replace_setarrayitem(self, op, livevars):
         if not var_needsgc(op.args[2]):
-            return [op]
+            return [op], []
         oldval = varoftype(op.args[2].concretetype)
         getoldvalop = SpaceOperation("getarrayitem",
                                      [op.args[0], op.args[1]], oldval)
         result = [getoldvalop]
         result.extend(self.push_alive(op.args[2]))
         result.append(op)
-        result.extend(self.pop_alive(oldval))
-        return result
+        return result, self.pop_alive(oldval)
 
     def get_rtti(self, TYPE):
         if isinstance(TYPE, lltype.GcStruct):
@@ -627,12 +627,46 @@
         else:
             self.finalizer_funcptrs[TYPE] = None
             return None
-        
+
+class FrameworkGCTransformer(BoehmGCTransformer):
+    #def __init__(self, translator):
+    #    super(FrameworkGCTransformer, self).__init__(translator)
+
+    def protect_roots(self, op, livevars):
+        livevars = [var for var in livevars if not var_ispyobj(var)]
+        newops = self.push_roots(livevars)
+        newops.append(op)
+        return newops, self.pop_roots(livevars)
+
+    replace_direct_call    = protect_roots
+    replace_indirect_call  = protect_roots
+    replace_malloc         = protect_roots
+    replace_malloc_varsize = protect_roots
+
+    def push_alive_nopyobj(self, var):
+        return []
+
+    def pop_alive_nopyobj(self, var):
+        return []
+
+    def push_roots(self, vars):
+        if vars:
+            return [SpaceOperation("gc_push_roots", vars, varoftype(lltype.Void))]
+        else:
+            return []
+
+    def pop_roots(self, vars):
+        if vars:
+            return [SpaceOperation("gc_pop_roots", vars, varoftype(lltype.Void))]
+        else:
+            return []
+
+
 # ___________________________________________________________________
 # calculate some statistics about the number of variables that need
 # to be cared for across a call
 
-relevant_ops = ["direct_call", "indirect_call", "malloc"]
+relevant_ops = ["direct_call", "indirect_call", "malloc", "malloc_varsize"]
 
 def filter_for_ptr(arg):
     return isinstance(arg.concretetype, lltype.Ptr)

Modified: pypy/dist/pypy/rpython/memory/test/test_gctransform.py
==============================================================================
--- pypy/dist/pypy/rpython/memory/test/test_gctransform.py	(original)
+++ pypy/dist/pypy/rpython/memory/test/test_gctransform.py	Wed Feb 15 23:52:39 2006
@@ -158,8 +158,8 @@
     ggraph = graphof(t, g)
     direct_calls = [op for op in ggraph.startblock.operations if op.opname == "direct_call"]
     assert len(direct_calls) == 3
-    assert direct_calls[1].cleanup[0].args[0] == direct_calls[0].result
-    assert [op.args[0] for op in direct_calls[2].cleanup] == \
+    assert direct_calls[1].cleanup[1][0].args[0] == direct_calls[0].result
+    assert [op.args[0] for op in direct_calls[2].cleanup[1]] == \
            [direct_calls[0].result, direct_calls[1].result]
 
 def test_multiply_passed_var():
@@ -563,3 +563,30 @@
     rel = gctransform.relevant_gcvars(t, gctransform.filter_for_nongcptr)
     print rel
     print sum(rel) / float(len(rel)), max(rel), min(rel)
+
+# ______________________________________________________________________
+# tests for FrameworkGCTransformer
+
+def test_framework_simple():
+    def g(x):
+        return x + 1
+    class A(object):
+        pass
+    def f():
+        a = A()
+        a.b = g(1)
+        return a.b
+    t, transformer = rtype_and_transform(f, [], gctransform.FrameworkGCTransformer, check=False)
+    graph = graphof(t, f)
+    calls = [(i, op) for i, op in enumerate(graph.startblock.operations)
+                     if op.opname == 'direct_call' and len(op.args) == 2]
+    assert len(calls) == 1
+    [(i, op)] = calls
+    finallyops, exceptops = op.cleanup
+    assert len(finallyops) == 1
+    assert len(exceptops) == 0
+    pushop = graph.startblock.operations[i-1]
+    [popop] = finallyops
+    assert pushop.opname == "gc_push_roots"
+    assert popop.opname  == "gc_pop_roots"
+    assert pushop.args == popop.args

Modified: pypy/dist/pypy/translator/c/funcgen.py
==============================================================================
--- pypy/dist/pypy/translator/c/funcgen.py	(original)
+++ pypy/dist/pypy/translator/c/funcgen.py	Wed Feb 15 23:52:39 2006
@@ -55,7 +55,7 @@
                 if hasattr(op, "cleanup"):
                     if op.cleanup is None:
                         continue
-                    for cleanupop in op.cleanup:
+                    for cleanupop in op.cleanup[0] + op.cleanup[1]:
                         mix.extend(cleanupop.args)
                         mix.append(cleanupop.result)
             for link in block.exits:
@@ -184,6 +184,11 @@
                 err   = 'err%d_%d' % (myblocknum, i)
                 for line in self.gen_op(op, err):
                     yield line
+                cleanup = getattr(op, 'cleanup', None)
+                if cleanup is not None:
+                    for subop in op.cleanup[0]:
+                        for line in self.gen_op(subop, "should_never_be_jumped_to2"):
+                            yield line
             fallthrough = False
             if len(block.exits) == 0:
                 if len(block.inputargs) == 2:   # exc_cls, exc_value
@@ -303,10 +308,10 @@
             for i, op in list(enumerate(block.operations))[::-1]:
                 if getattr(op, 'cleanup', None) is None:
                     continue
-                errorcases.setdefault(op.cleanup, []).append(i)
+                errorcases.setdefault(op.cleanup[1], []).append(i)
 
             if fallthrough:
-                firstclean = tuple(block.operations[-1].cleanup)
+                firstclean = tuple(block.operations[-1].cleanup[1])
                 first = errorcases[firstclean]
                 del errorcases[firstclean]
                 first.remove(len(block.operations) - 1)

Modified: pypy/dist/pypy/translator/tool/make_dot.py
==============================================================================
--- pypy/dist/pypy/translator/tool/make_dot.py	(original)
+++ pypy/dist/pypy/translator/tool/make_dot.py	Wed Feb 15 23:52:39 2006
@@ -118,7 +118,9 @@
     def visit_Block(self, block):
         # do the block itself
         name = self.blockname(block)
-        lines = map(repr, block.operations)
+        lines = []
+        for op in block.operations:
+            lines.extend(repr(op).split('\n'))
         lines.append("")
         numblocks = len(block.exits)
         color = "black"



More information about the Pypy-commit mailing list