[pypy-svn] r32577 - in pypy/branch/kill-keepalives/pypy: objspace/flow rpython rpython/lltypesystem rpython/memory translator/backendopt translator/c

mwh at codespeak.net mwh at codespeak.net
Fri Sep 22 15:00:30 CEST 2006


Author: mwh
Date: Fri Sep 22 15:00:25 2006
New Revision: 32577

Modified:
   pypy/branch/kill-keepalives/pypy/objspace/flow/model.py
   pypy/branch/kill-keepalives/pypy/rpython/llinterp.py
   pypy/branch/kill-keepalives/pypy/rpython/lltypesystem/lloperation.py
   pypy/branch/kill-keepalives/pypy/rpython/memory/gctransform.py
   pypy/branch/kill-keepalives/pypy/translator/backendopt/malloc.py
   pypy/branch/kill-keepalives/pypy/translator/c/funcgen.py
Log:
yet another and apparently working go at malloc removal of things with
autofree_fields.
this took an obscenely long time to get right.


Modified: pypy/branch/kill-keepalives/pypy/objspace/flow/model.py
==============================================================================
--- pypy/branch/kill-keepalives/pypy/objspace/flow/model.py	(original)
+++ pypy/branch/kill-keepalives/pypy/objspace/flow/model.py	Fri Sep 22 15:00:25 2006
@@ -42,7 +42,7 @@
 
 
 class FunctionGraph(object):
-    __slots__ = ['startblock', 'returnblock', 'exceptblock', '__dict__']
+    __slots__ = ['startblock', 'returnblock', 'exceptblock', '__dict__', 'needs_more_malloc_removal']
     
     def __init__(self, name, startblock, return_var=None):
         self.name        = name    # function name (possibly mangled already)
@@ -57,6 +57,7 @@
                                   Variable('evalue')])  # exception value
         self.exceptblock.operations = ()
         self.exceptblock.exits      = ()
+        self.needs_more_malloc_removal = False
 
     def getargs(self):
         return self.startblock.inputargs

Modified: pypy/branch/kill-keepalives/pypy/rpython/llinterp.py
==============================================================================
--- pypy/branch/kill-keepalives/pypy/rpython/llinterp.py	(original)
+++ pypy/branch/kill-keepalives/pypy/rpython/llinterp.py	Fri Sep 22 15:00:25 2006
@@ -171,7 +171,6 @@
         self.curr_block = None
         self.curr_operation_index = 0
         self.alloca_objects = []
-        self.local_mallocs = []
 
     # _______________________________________________________
     # variable setters/getters helpers
@@ -245,8 +244,6 @@
                     for obj in self.alloca_objects:
                         #XXX slighly unclean
                         obj._setobj(None)
-                    for adr in self.local_mallocs:
-                        self.heap.raw_free(adr)
                     return args
         finally:
             if tracer:
@@ -511,6 +508,7 @@
             setattr(obj, finalfield, fieldvalue)
         else:
             obj[finalfield] = fieldvalue
+    op_bare_setinteriorfield = op_setinteriorfield
 
     def op_getarrayitem(self, array, index):
         return array[index]
@@ -526,7 +524,7 @@
                 args = gc.get_arg_write_barrier(array, index, item)
                 write_barrier = gc.get_funcptr_write_barrier()
                 self.op_direct_call(write_barrier, *args)
-
+    op_bare_setarrayitem = op_setarrayitem
 
     def perform_call(self, f, ARGS, args):
         fobj = self.llinterpreter.typer.type_system.deref(f)
@@ -751,11 +749,6 @@
         assert lltype.typeOf(value) == typ
         getattr(addr, str(typ).lower())[offset] = value
 
-    def op_local_raw_malloc(self, size):
-        r = self.heap.raw_malloc(size)
-        self.local_mallocs.append(r)
-        return r
-
     # ____________________________________________________________
     # Overflow-detecting variants
 

Modified: pypy/branch/kill-keepalives/pypy/rpython/lltypesystem/lloperation.py
==============================================================================
--- pypy/branch/kill-keepalives/pypy/rpython/lltypesystem/lloperation.py	(original)
+++ pypy/branch/kill-keepalives/pypy/rpython/lltypesystem/lloperation.py	Fri Sep 22 15:00:25 2006
@@ -329,7 +329,6 @@
     # __________ address operations __________
 
     'raw_malloc':           LLOp(canraise=(MemoryError,)),
-    'local_raw_malloc':     LLOp(canraise=(MemoryError,)),
     'raw_malloc_usage':     LLOp(sideeffects=False),
     'raw_free':             LLOp(),
     'raw_memclear':         LLOp(),

Modified: pypy/branch/kill-keepalives/pypy/rpython/memory/gctransform.py
==============================================================================
--- pypy/branch/kill-keepalives/pypy/rpython/memory/gctransform.py	(original)
+++ pypy/branch/kill-keepalives/pypy/rpython/memory/gctransform.py	Fri Sep 22 15:00:25 2006
@@ -81,7 +81,6 @@
             return
         self.seen_graphs[graph] = True
         self.links_to_split = {} # link -> vars to pop_alive across the link
-        self.seen_local_raw_malloc = False
 
         # for sanity, we need an empty block at the start of the graph
         if not starts_with_empty_block(graph):
@@ -108,68 +107,12 @@
         if starts_with_empty_block(graph):
             remove_empty_startblock(graph)
 
-        if self.seen_local_raw_malloc:
-            self.remove_local_mallocs(graph)
-
         self.links_to_split = None
         v = Variable('vanishing_exc_value')
         v.concretetype = self.get_lltype_of_exception_value()
         graph.exc_cleanup = (v, self.pop_alive(v))
         return is_borrowed    # xxx for tests only
 
-    def remove_local_mallocs(self, graph):
-        from pypy.translator.backendopt.malloc import compute_lifetimes
-        lifetimes = compute_lifetimes(graph)
-        for info in lifetimes:
-            cand = True
-            # XXX do checking
-            for cp in info.creationpoints:
-                if cp[0] != "op":
-                    cand = False
-                    break
-                op = cp[2]
-                if op.opname != 'local_raw_malloc':
-                    cand = False
-                    break
-                op.opname = 'raw_malloc'
-            if cand:
-                for cp in info.creationpoints:
-                    cp[2].opname = 'raw_malloc'
-
-                variables_by_block = {}
-                for block, var in info.variables:
-                    vars = variables_by_block.setdefault(block, set())
-                    vars.add(var)
-                for block, vars in variables_by_block.iteritems():
-                    links_with_a_var = []
-                    links_without_a_var = []
-                    for link in block.exits:
-                        if vars & set(link.args):
-                            links_with_a_var.append(link)
-                        else:
-                            links_without_a_var.append(link)
-                    #if not links_without_a_var:
-                    #    continue
-                    for link in links_without_a_var:
-                        vv = iter(vars).next()
-                        for v in vars:
-                            assert v not in link.args
-                            if v.concretetype == llmemory.Address:
-                                vv = v
-                        newblock = insert_empty_block(None, link)
-                        link.args.append(vv)
-                        newblock.inputargs.append(copyvar(None, vv))
-                        if vv.concretetype != llmemory.Address:
-                            newv = varoftype(llmemory.Address)
-                            newblock.operations.append(SpaceOperation(
-                                "cast_ptr_to_adr", [newblock.inputargs[-1]], newv))
-                            vv = newv
-                        else:
-                            vv = newblock.inputargs[-1]
-                        newblock.operations.append(SpaceOperation(
-                            "raw_free", [vv], varoftype(lltype.Void)))
-        checkgraph(graph)
-
     def compute_borrowed_vars(self, graph):
         # the input args are borrowed, and stay borrowed for as long as they
         # are not merged with other values.
@@ -328,10 +271,6 @@
     replace_setinteriorfield = replace_setfield
     replace_setarrayitem = replace_setfield
 
-    def replace_local_raw_malloc(self, op, livevars, block):
-        self.seen_local_raw_malloc = True
-        return [op]
-
     def replace_safe_call(self, op, livevars, block):
         return [SpaceOperation("direct_call", op.args, op.result)]
 

Modified: pypy/branch/kill-keepalives/pypy/translator/backendopt/malloc.py
==============================================================================
--- pypy/branch/kill-keepalives/pypy/translator/backendopt/malloc.py	(original)
+++ pypy/branch/kill-keepalives/pypy/translator/backendopt/malloc.py	Fri Sep 22 15:00:25 2006
@@ -1,9 +1,9 @@
 from pypy.objspace.flow.model import Variable, Constant, Block, Link
-from pypy.objspace.flow.model import SpaceOperation, traverse
+from pypy.objspace.flow.model import SpaceOperation, traverse, checkgraph
 from pypy.tool.algo.unionfind import UnionFind
 from pypy.rpython.lltypesystem import lltype
-from pypy.translator.simplify import remove_identical_vars
-from pypy.translator.unsimplify import varoftype
+from pypy.translator.simplify import remove_identical_vars, join_blocks
+from pypy.translator.unsimplify import varoftype, insert_empty_block, copyvar
 from pypy.translator.backendopt.support import log
 from pypy.translator.backendopt.constfold import constant_fold_graph
 
@@ -87,7 +87,7 @@
     def visit(node):
         if isinstance(node, Block):
             for op in node.operations:
-                if op.opname in ("same_as", "cast_pointer", "cast_adr_to_ptr"):
+                if op.opname in ("same_as", "cast_pointer"):
                     # special-case these operations to identify their input
                     # and output variables
                     union(node, op.args[0], node, op.result)
@@ -128,7 +128,7 @@
     traverse(visit, graph)
     return lifetimes.infos()
 
-def _try_inline_malloc(info):
+def _try_inline_malloc(graph, info, links_to_split, remove_autofrees=False):
     """Try to inline the mallocs creation and manipulation of the Variables
     in the given LifeTime."""
     # the values must be only ever created by a "malloc"
@@ -220,18 +220,29 @@
 
     try:
         destr_ptr = lltype.getRuntimeTypeInfo(STRUCT)._obj.destructor_funcptr
-        if destr_ptr and 'autofree_fields' not in STRUCT._hints:
-            return False
-        fields_to_raw_free = STRUCT._hints['autofree_fields']
+        if destr_ptr:
+            if 'autofree_fields' in STRUCT._hints:
+                if remove_autofrees:
+                    fields_to_raw_free = STRUCT._hints['autofree_fields']
+                    assert len(info.creationpoints) == 1
+                else:
+                    graph.needs_more_malloc_removal = True
+                    return False
+            else:
+                return False
     except (ValueError, AttributeError), e:
         pass
 
+    assert not fields_to_raw_free or remove_autofrees
+
     # must not remove unions inlined as the only field of a GcStruct
     if union_wrapper(STRUCT):
         return False
 
     # success: replace each variable with a family of variables (one per field)
 
+    # print 'removing malloc of', STRUCT, fields_to_raw_free
+
     # 'flatnames' is a list of (STRUCTTYPE, fieldname_in_that_struct) that
     # describes the list of variables that should replace the single
     # malloc'ed pointer variable that we are about to remove.  For primitive
@@ -306,6 +317,16 @@
 
     for block, vars in variables_by_block.items():
 
+        links_without_vars = []
+
+        if fields_to_raw_free:
+            for link in block.exits:
+                if not set(link.args) & set(vars):
+                    links_without_vars.append(link)
+##                     print "link hasn't var", link, 'args:', link.args, 'vars', vars
+##                     print [(c, c.concretetype) for c in link.args]
+##                     print [(c, c.concretetype) for c in vars]
+
         def flowin(var, newvarsmap, insert_keepalive=False):
             # in this 'block', follow where the 'var' goes to and replace
             # it by a flattened-out family of variables.  This family is given
@@ -341,15 +362,6 @@
                         S = op.args[0].concretetype.TO
                         fldnames = [a.value for a in op.args[1:-1]]
                         key = key_for_field_access(S, *fldnames)
-                        if len(fldnames) == 1 and fldnames[0] in fields_to_raw_free and not isinstance(op.args[2], Constant):
-                            # find the raw malloc and replace it with a local_raw_malloc
-                            # XXX delicate in the extreme!
-                            i = -1
-                            while newops[i].opname != 'raw_malloc':
-                                i -= 1
-                            newops[i] = SpaceOperation("local_raw_malloc",
-                                                       newops[i].args,
-                                                       newops[i].result)
                         assert key in newvarsmap
                         if key in accessed_substructs:
                             c_name = Constant('data', lltype.Void)
@@ -423,28 +435,22 @@
                     count[0] += progress
                 else:
                     newops.append(op)
+##                 if newops[-1:] != [op] and remove_autofrees:
+##                     newops.append(SpaceOperation("debug_print", [Constant(str(op), lltype.Void)], varoftype(lltype.Void)))
 
             assert block.exitswitch not in vars
 
-            var_exits = False
             for link in block.exits:
                 newargs = []
+                oldargs = link.args[:]
                 for arg in link.args:
                     if arg in vars:
                         newargs += list_newvars()
                         insert_keepalive = False   # kept alive by the link
-                        var_exits = True
                     else:
                         newargs.append(arg)
                 link.args[:] = newargs
 
-##             if not var_exits:
-##                 for field in fields_to_raw_free:
-##                     newops.append(SpaceOperation("flavored_free",
-##                                                  [Constant("raw", lltype.Void),
-##                                                   newvarsmap[key_for_field_access(STRUCT, field)]],
-##                                                  varoftype(lltype.Void)))
-
             if insert_keepalive and last_removed_access is not None:
                 keepalives = []
                 for v in list_newvars():
@@ -458,6 +464,10 @@
 
             block.operations[:] = newops
 
+            return newvarsmap
+
+        for_field_freeing_varmap = None
+
         # look for variables arriving from outside the block
         for var in vars:
             if var in block.inputargs:
@@ -465,14 +475,14 @@
                 newinputargs = block.inputargs[:i]
                 newvarsmap = {}
                 for key in flatnames:
-                    newvar = Variable()
+                    newvar = Variable(key[0]._name + ''.join(map(str, key[1:])))
                     newvar.concretetype = newvarstype[key]
                     newvarsmap[key] = newvar
                     newinputargs.append(newvar)
                 newinputargs += block.inputargs[i+1:]
                 block.inputargs[:] = newinputargs
                 assert var not in block.inputargs
-                flowin(var, newvarsmap, insert_keepalive=True)
+                for_field_freeing_varmap = flowin(var, newvarsmap, insert_keepalive=True)
 
         # look for variables created inside the block by a malloc
         vars_created_here = []
@@ -480,24 +490,50 @@
             if op.opname in ("malloc", "zero_malloc") and op.result in vars:
                 vars_created_here.append(op.result)
         for var in vars_created_here:
-            flowin(var, newvarsmap=None)
+            newmap = flowin(var, newvarsmap=None)
+        if for_field_freeing_varmap is None:
+            for_field_freeing_varmap = newmap
+
+        if fields_to_raw_free and links:
+            for link in links_without_vars:
+                varstofree = [for_field_freeing_varmap[key_for_field_access(STRUCT, field)] for field in fields_to_raw_free]
+                varstofree = [v for v in varstofree if isinstance(v, Variable)]
+                if varstofree:
+                    links_to_split.setdefault(link, []).extend(varstofree)
 
     return count[0]
 
-def remove_mallocs_once(graph):
+def remove_mallocs_once(graph, remove_autofrees=False):
     """Perform one iteration of malloc removal."""
     remove_identical_vars(graph)
     lifetimes = compute_lifetimes(graph)
     progress = 0
+    links_to_split = {}
     for info in lifetimes:
-        progress += _try_inline_malloc(info)
+        progress += _try_inline_malloc(graph, info, links_to_split, remove_autofrees)
+        checkgraph(graph)
+        join_blocks(graph)
+    for link, vars_to_free in links_to_split.iteritems():
+        newblock = None
+        for v in vars_to_free:
+            if isinstance(v, Variable):
+                if not newblock:
+                    newblock = insert_empty_block(None, link)
+                link.args.append(v)
+                newblock.inputargs.append(copyvar(None, v))
+                newblock.operations.append(SpaceOperation("flavored_free",
+                                                          [Constant("raw", lltype.Void),
+                                                           newblock.inputargs[-1]],
+                                                          varoftype(lltype.Void)))
+##     if graph.name == "func" and remove_autofrees and progress:
+##         graph.show()
     return progress
 
-def remove_simple_mallocs(graph, callback=None):
+def remove_simple_mallocs(graph, callback=None, remove_autofrees=False):
     """Iteratively remove (inline) the mallocs that can be simplified away."""
     tot = 0
     while True:
-        count = remove_mallocs_once(graph)
+        count = remove_mallocs_once(graph, remove_autofrees)
         if count:
             log.malloc('%d simple mallocs removed in %r' % (count, graph.name))
             constant_fold_graph(graph)

Modified: pypy/branch/kill-keepalives/pypy/translator/c/funcgen.py
==============================================================================
--- pypy/branch/kill-keepalives/pypy/translator/c/funcgen.py	(original)
+++ pypy/branch/kill-keepalives/pypy/translator/c/funcgen.py	Fri Sep 22 15:00:25 2006
@@ -48,6 +48,9 @@
         # apply the exception transformation
         if self.db.exctransformer:
             self.db.exctransformer.create_exception_handling(self.graph)
+        if graph.needs_more_malloc_removal:
+            from pypy.translator.backendopt.malloc import remove_simple_mallocs
+            remove_simple_mallocs(graph, remove_autofrees=True)
         # apply the gc transformation
         if self.db.gctransformer:
             self.db.gctransformer.transform_graph(self.graph)
@@ -753,6 +756,9 @@
                     format.append(arg.value.replace('%', '%%'))
                     continue
                 format.append('%c')
+            elif T == Void and isinstance(arg, Constant) and isinstance(arg.value, str):
+                format.append(arg.value.replace('%', '%%'))
+                continue                
             else:
                 raise Exception("don't know how to debug_print %r" % (T,))
             argv.append(self.expr(arg))



More information about the Pypy-commit mailing list