[pypy-svn] r36035 - in pypy/dist/pypy/translator/backendopt: . test

antocuni at codespeak.net antocuni at codespeak.net
Fri Dec 29 14:34:43 CET 2006


Author: antocuni
Date: Fri Dec 29 14:34:40 2006
New Revision: 36035

Modified:
   pypy/dist/pypy/translator/backendopt/malloc.py
   pypy/dist/pypy/translator/backendopt/test/test_malloc.py
Log:
First steps in making malloc removal ootype-compatible.



Modified: pypy/dist/pypy/translator/backendopt/malloc.py
==============================================================================
--- pypy/dist/pypy/translator/backendopt/malloc.py	(original)
+++ pypy/dist/pypy/translator/backendopt/malloc.py	Fri Dec 29 14:34:40 2006
@@ -18,138 +18,246 @@
         self.creationpoints.update(other.creationpoints)
         self.usepoints.update(other.usepoints)
 
+class BaseMallocRemover(object):
 
-def equivalent_substruct(S, fieldname):
-    # we consider a pointer to a GcStruct S as equivalent to a
-    # pointer to a substructure 'S.fieldname' if it's the first
-    # inlined sub-GcStruct.  As an extension we also allow a pointer
-    # to a GcStruct containing just one field to be equivalent to
-    # a pointer to that field only (although a mere cast_pointer
-    # would not allow casting).  This is needed to malloc-remove
-    # the 'wrapper' GcStructs introduced by previous passes of
-    # malloc removal.
-    if not isinstance(S, lltype.GcStruct):
-        return False
-    if fieldname != S._names[0]:
+    IDENTITY_OPS = ('same_as',)
+    SUBSTRUCT_OPS = ()
+    MALLOC_OP = None
+    FIELD_ACCESS = {}
+    SUBSTRUCT_ACCESS = {}
+    CHECK_ARRAY_INDEX = {}
+
+    def get_STRUCT(self, TYPE):
+        raise NotImplementedError
+
+    def do_substruct_access(self, op):
+        raise NotImplementedError
+
+    def equivalent_substruct(self, S, fieldname):
+        raise NotImplementedError
+
+    def union_wrapper(self, S):
         return False
-    FIELDTYPE = S._flds[fieldname]
-    if isinstance(FIELDTYPE, lltype.GcStruct):
-        if FIELDTYPE._hints.get('union'):
-            return False
-        return True
-    if len(S._names) == 1:
-        return True
-    return False
-
-def union_wrapper(S):
-    # check if 'S' is a GcStruct containing a single inlined *union* Struct
-    if not isinstance(S, lltype.GcStruct):
+
+    def RTTI_dtor(self, STRUCT):
         return False
-    assert not S._hints.get('union')    # not supported: "GcUnion"
-    return (len(S._names) == 1 and
-            isinstance(S._flds[S._names[0]], lltype.Struct) and
-            S._flds[S._names[0]]._hints.get('union'))
-
-
-def compute_lifetimes(graph):
-    """Compute the static data flow of the graph: returns a list of LifeTime
-    instances, each of which corresponds to a set of Variables from the graph.
-    The variables are grouped in the same LifeTime if a value can pass from
-    one to the other by following the links.  Each LifeTime also records all
-    places where a Variable in the set is used (read) or build (created).
-    """
-    lifetimes = UnionFind(LifeTime)
-
-    def set_creation_point(block, var, *cp):
-        _, _, info = lifetimes.find((block, var))
-        info.creationpoints[cp] = True
-
-    def set_use_point(block, var, *up):
-        _, _, info = lifetimes.find((block, var))
-        info.usepoints[up] = True
-
-    def union(block1, var1, block2, var2):
-        if isinstance(var1, Variable):
-            lifetimes.union((block1, var1), (block2, var2))
-        elif isinstance(var1, Constant):
-            set_creation_point(block2, var2, "constant", var1)
-        else:
-            raise TypeError(var1)
-
-    for var in graph.startblock.inputargs:
-        set_creation_point(graph.startblock, var, "inputargs")
-    set_use_point(graph.returnblock, graph.returnblock.inputargs[0], "return")
-    set_use_point(graph.exceptblock, graph.exceptblock.inputargs[0], "except")
-    set_use_point(graph.exceptblock, graph.exceptblock.inputargs[1], "except")
-
-    def visit(node):
-        if isinstance(node, Block):
-            for op in node.operations:
-                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)
-                    continue
-                if op.opname in ('getsubstruct', 'direct_fieldptr'):
-                    S = op.args[0].concretetype.TO
-                    if equivalent_substruct(S, op.args[1].value):
-                        # assumed to be similar to a cast_pointer
+
+    def flatten(self, S):
+        raise NotImplementedError
+
+    def key_for_field_access(self, S, fldname):
+        raise NotImplementedError
+
+    def flowin(self, block, count, var, newvarsmap):
+        raise NotImplementedError
+
+    def compute_lifetimes(self, graph):
+        """Compute the static data flow of the graph: returns a list of LifeTime
+        instances, each of which corresponds to a set of Variables from the graph.
+        The variables are grouped in the same LifeTime if a value can pass from
+        one to the other by following the links.  Each LifeTime also records all
+        places where a Variable in the set is used (read) or build (created).
+        """
+        lifetimes = UnionFind(LifeTime)
+
+        def set_creation_point(block, var, *cp):
+            _, _, info = lifetimes.find((block, var))
+            info.creationpoints[cp] = True
+
+        def set_use_point(block, var, *up):
+            _, _, info = lifetimes.find((block, var))
+            info.usepoints[up] = True
+
+        def union(block1, var1, block2, var2):
+            if isinstance(var1, Variable):
+                lifetimes.union((block1, var1), (block2, var2))
+            elif isinstance(var1, Constant):
+                set_creation_point(block2, var2, "constant", var1)
+            else:
+                raise TypeError(var1)
+
+        for var in graph.startblock.inputargs:
+            set_creation_point(graph.startblock, var, "inputargs")
+        set_use_point(graph.returnblock, graph.returnblock.inputargs[0], "return")
+        set_use_point(graph.exceptblock, graph.exceptblock.inputargs[0], "except")
+        set_use_point(graph.exceptblock, graph.exceptblock.inputargs[1], "except")
+
+        def visit(node):
+            if isinstance(node, Block):
+                for op in node.operations:
+                    if op.opname in self.IDENTITY_OPS:
+                        # special-case these operations to identify their input
+                        # and output variables
                         union(node, op.args[0], node, op.result)
                         continue
-                for i in range(len(op.args)):
-                    if isinstance(op.args[i], Variable):
-                        set_use_point(node, op.args[i], "op", node, op, i)
-                set_creation_point(node, op.result, "op", node, op)
-            if isinstance(node.exitswitch, Variable):
-                set_use_point(node, node.exitswitch, "exitswitch", node)
-
-        if isinstance(node, Link):
-            if isinstance(node.last_exception, Variable):
-                set_creation_point(node.prevblock, node.last_exception,
-                                   "last_exception")
-            if isinstance(node.last_exc_value, Variable):
-                set_creation_point(node.prevblock, node.last_exc_value,
-                                   "last_exc_value")
-            d = {}
-            for i, arg in enumerate(node.args):
-                union(node.prevblock, arg,
-                      node.target, node.target.inputargs[i])
-                if isinstance(arg, Variable):
-                    if arg in d:
-                        # same variable present several times in link.args
-                        # consider it as a 'use' of the variable, which
-                        # will disable malloc optimization (aliasing problems)
-                        set_use_point(node.prevblock, arg, "dup", node, i)
-                    else:
-                        d[arg] = True
+                    if op.opname in self.SUBSTRUCT_OPS:
+                        S = op.args[0].concretetype.TO
+                        if self.equivalent_substruct(S, op.args[1].value):
+                            # assumed to be similar to a cast_pointer
+                            union(node, op.args[0], node, op.result)
+                            continue
+                    for i in range(len(op.args)):
+                        if isinstance(op.args[i], Variable):
+                            set_use_point(node, op.args[i], "op", node, op, i)
+                    set_creation_point(node, op.result, "op", node, op)
+                if isinstance(node.exitswitch, Variable):
+                    set_use_point(node, node.exitswitch, "exitswitch", node)
+
+            if isinstance(node, Link):
+                if isinstance(node.last_exception, Variable):
+                    set_creation_point(node.prevblock, node.last_exception,
+                                       "last_exception")
+                if isinstance(node.last_exc_value, Variable):
+                    set_creation_point(node.prevblock, node.last_exc_value,
+                                       "last_exc_value")
+                d = {}
+                for i, arg in enumerate(node.args):
+                    union(node.prevblock, arg,
+                          node.target, node.target.inputargs[i])
+                    if isinstance(arg, Variable):
+                        if arg in d:
+                            # same variable present several times in link.args
+                            # consider it as a 'use' of the variable, which
+                            # will disable malloc optimization (aliasing problems)
+                            set_use_point(node.prevblock, arg, "dup", node, i)
+                        else:
+                            d[arg] = True
 
-    traverse(visit, graph)
-    return lifetimes.infos()
+        traverse(visit, graph)
+        return lifetimes.infos()
 
-def _try_inline_malloc(info):
-    """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"
-    lltypes = {}
-    for cp in info.creationpoints:
-        if cp[0] != "op":
+    def _try_inline_malloc(self, info):
+        """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"
+        lltypes = {}
+        for cp in info.creationpoints:
+            if cp[0] != "op":
+                return False
+            op = cp[2]
+            if op.opname != self.MALLOC_OP:
+                return False
+            lltypes[op.result.concretetype] = True
+
+        # there must be a single largest malloced GcStruct;
+        # all variables can point to it or to initial substructures
+        if len(lltypes) != 1:
             return False
-        op = cp[2]
-        if op.opname != "malloc":
+        STRUCT = self.get_STRUCT(lltypes.keys()[0])
+
+        # must be only ever accessed via getfield/setfield/getsubstruct/
+        # direct_fieldptr, or touched by keepalive or ptr_iszero/ptr_nonzero.
+        # Note that same_as and cast_pointer are not recorded in usepoints.
+        self.accessed_substructs = {}
+
+        for up in info.usepoints:
+            if up[0] != "op":
+                return False
+            kind, node, op, index = up
+            if index != 0:
+                return False
+            if op.opname in self.CHECK_ARRAY_INDEX:
+                if not isinstance(op.args[1], Constant):
+                    return False    # non-constant array index
+            if op.opname in self.FIELD_ACCESS:
+                pass   # ok
+            elif op.opname in self.SUBSTRUCT_ACCESS:
+                self.do_substruct_access(op)
+            else:
+                return False
+
+        # must not remove mallocs of structures that have a RTTI with a destructor
+        if self.RTTI_dtor(STRUCT):
             return False
-        lltypes[op.result.concretetype] = True
 
-    # there must be a single largest malloced GcStruct;
-    # all variables can point to it or to initial substructures
-    if len(lltypes) != 1:
-        return False
-    STRUCT = lltypes.keys()[0].TO
-    assert isinstance(STRUCT, lltype.GcStruct)
+        # must not remove unions inlined as the only field of a GcStruct
+        if self.union_wrapper(STRUCT):
+            return False
+
+        # success: replace each variable with a family of variables (one per field)
+
+        # '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
+        # or pointer fields, the new corresponding variable just stores the
+        # actual value.  For substructures, if pointers to them are "equivalent"
+        # to pointers to the parent structure (see equivalent_substruct()) then
+        # they are just merged, and flatnames will also list the fields within
+        # that substructure.  Other substructures are replaced by a single new
+        # variable which is a pointer to a GcStruct-wrapper; each is malloc'ed
+        # individually, in an exploded way.  (The next malloc removal pass will
+        # get rid of them again, in the typical case.)
+        self.flatnames = []
+        self.flatconstants = {}
+        self.needsubmallocs = []
+        self.newvarstype = {}       # map {item-of-flatnames: concretetype}
+        self.direct_fieldptr_key = {}
+        self.flatten(STRUCT)
+        assert len(self.direct_fieldptr_key) <= 1
+
+        variables_by_block = {}
+        for block, var in info.variables:
+            vars = variables_by_block.setdefault(block, {})
+            vars[var] = True
+
+        count = [0]
+
+        for block, vars in variables_by_block.items():
+
+            # look for variables arriving from outside the block
+            for var in vars:
+                if var in block.inputargs:
+                    i = block.inputargs.index(var)
+                    newinputargs = block.inputargs[:i]
+                    newvarsmap = {}
+                    for key in self.flatnames:
+                        newvar = Variable()
+                        newvar.concretetype = self.newvarstype[key]
+                        newvarsmap[key] = newvar
+                        newinputargs.append(newvar)
+                    newinputargs += block.inputargs[i+1:]
+                    block.inputargs[:] = newinputargs
+                    assert var not in block.inputargs
+                    self.flowin(block, count, var, newvarsmap)
+
+            # look for variables created inside the block by a malloc
+            vars_created_here = []
+            for op in block.operations:
+                if op.opname == self.MALLOC_OP and op.result in vars:
+                    vars_created_here.append(op.result)
+            for var in vars_created_here:
+                self.flowin(block, count, var, newvarsmap=None)
+
+        return count[0]
+
+    def remove_mallocs_once(self, graph):
+        """Perform one iteration of malloc removal."""
+        remove_identical_vars(graph)
+        lifetimes = self.compute_lifetimes(graph)
+        progress = 0
+        for info in lifetimes:
+            progress += self._try_inline_malloc(info)
+        return progress
+
+    def remove_simple_mallocs(self, graph):
+        """Iteratively remove (inline) the mallocs that can be simplified away."""
+        tot = 0
+        while True:
+            count = self.remove_mallocs_once(graph)
+            if count:
+                log.malloc('%d simple mallocs removed in %r' % (count, graph.name))
+                tot += count
+            else:
+                break
+        return tot
+
+
+class LLTypeMallocRemover(BaseMallocRemover):
 
-    # must be only ever accessed via getfield/setfield/getsubstruct/
-    # direct_fieldptr, or touched by keepalive or ptr_iszero/ptr_nonzero.
-    # Note that same_as and cast_pointer are not recorded in usepoints.
-    FIELD_ACCESS     = dict.fromkeys(["getfield",
+    IDENTITY_OPS = ("same_as", "cast_pointer")
+    SUBSTRUCT_OPS = ("getsubstruct", "direct_fieldptr")
+    MALLOC_OP = "malloc"
+    FIELD_ACCESS =     dict.fromkeys(["getfield",
                                       "setfield",
                                       "keepalive",
                                       "ptr_iszero",
@@ -159,290 +267,240 @@
     SUBSTRUCT_ACCESS = dict.fromkeys(["getsubstruct",
                                       "direct_fieldptr",
                                       "getarraysubstruct"])
-
     CHECK_ARRAY_INDEX = dict.fromkeys(["getarrayitem",
                                        "setarrayitem",
                                        "getarraysubstruct"])
-    accessed_substructs = {}
 
-    for up in info.usepoints:
-        if up[0] != "op":
-            return False
-        kind, node, op, index = up
-        if index != 0:
+    def get_STRUCT(self, TYPE):
+        STRUCT = TYPE.TO
+        assert isinstance(STRUCT, lltype.GcStruct)
+        return STRUCT
+
+    def do_substruct_access(self, op):
+        S = op.args[0].concretetype.TO
+        name = op.args[1].value
+        if not isinstance(name, str):      # access by index
+            name = 'item%d' % (name,)
+        self.accessed_substructs[S, name] = True
+
+    def equivalent_substruct(self, S, fieldname):
+        # we consider a pointer to a GcStruct S as equivalent to a
+        # pointer to a substructure 'S.fieldname' if it's the first
+        # inlined sub-GcStruct.  As an extension we also allow a pointer
+        # to a GcStruct containing just one field to be equivalent to
+        # a pointer to that field only (although a mere cast_pointer
+        # would not allow casting).  This is needed to malloc-remove
+        # the 'wrapper' GcStructs introduced by previous passes of
+        # malloc removal.
+        if not isinstance(S, lltype.GcStruct):
             return False
-        if op.opname in CHECK_ARRAY_INDEX:
-            if not isinstance(op.args[1], Constant):
-                return False    # non-constant array index
-        if op.opname in FIELD_ACCESS:
-            pass   # ok
-        elif op.opname in SUBSTRUCT_ACCESS:
-            S = op.args[0].concretetype.TO
-            name = op.args[1].value
-            if not isinstance(name, str):      # access by index
-                name = 'item%d' % (name,)
-            accessed_substructs[S, name] = True
-        else:
+        if fieldname != S._names[0]:
             return False
+        FIELDTYPE = S._flds[fieldname]
+        if isinstance(FIELDTYPE, lltype.GcStruct):
+            if FIELDTYPE._hints.get('union'):
+                return False
+            return True
+        if len(S._names) == 1:
+            return True
+        return False
 
-    # must not remove mallocs of structures that have a RTTI with a destructor
-
-    try:
-        destr_ptr = lltype.getRuntimeTypeInfo(STRUCT)._obj.destructor_funcptr
-        if destr_ptr:
+    def union_wrapper(self, S):
+        # check if 'S' is a GcStruct containing a single inlined *union* Struct
+        if not isinstance(S, lltype.GcStruct):
             return False
-    except (ValueError, AttributeError), e:
-        pass
+        assert not S._hints.get('union')    # not supported: "GcUnion"
+        return (len(S._names) == 1 and
+                isinstance(S._flds[S._names[0]], lltype.Struct) and
+                S._flds[S._names[0]]._hints.get('union'))
 
-    # 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)
 
-    # '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
-    # or pointer fields, the new corresponding variable just stores the
-    # actual value.  For substructures, if pointers to them are "equivalent"
-    # to pointers to the parent structure (see equivalent_substruct()) then
-    # they are just merged, and flatnames will also list the fields within
-    # that substructure.  Other substructures are replaced by a single new
-    # variable which is a pointer to a GcStruct-wrapper; each is malloc'ed
-    # individually, in an exploded way.  (The next malloc removal pass will
-    # get rid of them again, in the typical case.)
-    flatnames = []
-    flatconstants = {}
-    needsubmallocs = []
-    newvarstype = {}       # map {item-of-flatnames: concretetype}
-    direct_fieldptr_key = {}
+    def RTTI_dtor(self, STRUCT):
+        try:
+            destr_ptr = lltype.getRuntimeTypeInfo(STRUCT)._obj.destructor_funcptr
+            if destr_ptr:
+                return True
+        except (ValueError, AttributeError), e:
+            pass
+        return False
 
-    def flatten(S):
+    def flatten(self, S):
         start = 0
-        if S._names and equivalent_substruct(S, S._names[0]):
+        if S._names and self.equivalent_substruct(S, S._names[0]):
             SUBTYPE = S._flds[S._names[0]]
             if isinstance(SUBTYPE, lltype.Struct):
-                flatten(SUBTYPE)
+                self.flatten(SUBTYPE)
                 start = 1
             else:
                 ARRAY = lltype.FixedSizeArray(SUBTYPE, 1)
-                direct_fieldptr_key[ARRAY, 'item0'] = S, S._names[0]
+                self.direct_fieldptr_key[ARRAY, 'item0'] = S, S._names[0]
         for name in S._names[start:]:
             key = S, name
             FIELDTYPE = S._flds[name]
-            if key in accessed_substructs:
-                needsubmallocs.append(key)
-                flatnames.append(key)
-                newvarstype[key] = lltype.Ptr(lltype.GcStruct('wrapper',
+            if key in self.accessed_substructs:
+                self.needsubmallocs.append(key)
+                self.flatnames.append(key)
+                self.newvarstype[key] = lltype.Ptr(lltype.GcStruct('wrapper',
                                                           ('data', FIELDTYPE)))
             elif not isinstance(FIELDTYPE, lltype.ContainerType):
                 example = FIELDTYPE._defl()
                 constant = Constant(example)
                 constant.concretetype = FIELDTYPE
-                flatconstants[key] = constant
-                flatnames.append(key)
-                newvarstype[key] = FIELDTYPE
+                self.flatconstants[key] = constant
+                self.flatnames.append(key)
+                self.newvarstype[key] = FIELDTYPE
             #else:
             #   the inlined substructure is never accessed, drop it
 
-    flatten(STRUCT)
-    assert len(direct_fieldptr_key) <= 1
-
-    def key_for_field_access(S, fldname):
+    def key_for_field_access(self, S, fldname):
         if isinstance(S, lltype.FixedSizeArray):
             if not isinstance(fldname, str):      # access by index
                 fldname = 'item%d' % (fldname,)
             try:
-                return direct_fieldptr_key[S, fldname]
+                return self.direct_fieldptr_key[S, fldname]
             except KeyError:
                 pass
         return S, fldname
 
-    variables_by_block = {}
-    for block, var in info.variables:
-        vars = variables_by_block.setdefault(block, {})
-        vars[var] = True
-
-    count = [0]
-
-    for block, vars in variables_by_block.items():
-
-        def flowin(var, newvarsmap):
-            # in this 'block', follow where the 'var' goes to and replace
-            # it by a flattened-out family of variables.  This family is given
-            # by newvarsmap, whose keys are the 'flatnames'.
-            vars = {var: True}
-            last_removed_access = None
+    def flowin(self, block, count, var, newvarsmap):
+        # in this 'block', follow where the 'var' goes to and replace
+        # it by a flattened-out family of variables.  This family is given
+        # by newvarsmap, whose keys are the 'flatnames'.
+        vars = {var: True}
+        last_removed_access = None
 
-            def list_newvars():
-                return [newvarsmap[key] for key in flatnames]
+        def list_newvars():
+            return [newvarsmap[key] for key in self.flatnames]
 
-            assert block.operations != ()
-            newops = []
-            for op in block.operations:
-                for arg in op.args[1:]:   # should be the first arg only
-                    assert arg not in vars
-                if op.args and op.args[0] in vars:
-                    if op.opname in ("getfield", "getarrayitem"):
-                        S = op.args[0].concretetype.TO
-                        fldname = op.args[1].value
-                        key = key_for_field_access(S, fldname)
-                        if key in accessed_substructs:
-                            c_name = Constant('data', lltype.Void)
-                            newop = SpaceOperation("getfield",
-                                                   [newvarsmap[key], c_name],
-                                                   op.result)
-                        else:
-                            newop = SpaceOperation("same_as",
-                                                   [newvarsmap[key]],
-                                                   op.result)
+        assert block.operations != ()
+        newops = []
+        for op in block.operations:
+            for arg in op.args[1:]:   # should be the first arg only
+                assert arg not in vars
+            if op.args and op.args[0] in vars:
+                if op.opname in ("getfield", "getarrayitem"):
+                    S = op.args[0].concretetype.TO
+                    fldname = op.args[1].value
+                    key = self.key_for_field_access(S, fldname)
+                    if key in self.accessed_substructs:
+                        c_name = Constant('data', lltype.Void)
+                        newop = SpaceOperation("getfield",
+                                               [newvarsmap[key], c_name],
+                                               op.result)
+                    else:
+                        newop = SpaceOperation("same_as",
+                                               [newvarsmap[key]],
+                                               op.result)
+                    newops.append(newop)
+                    last_removed_access = len(newops)
+                elif op.opname in ("setfield", "setarrayitem"):
+                    S = op.args[0].concretetype.TO
+                    fldname = op.args[1].value
+                    key = self.key_for_field_access(S, fldname)
+                    assert key in newvarsmap
+                    if key in self.accessed_substructs:
+                        c_name = Constant('data', lltype.Void)
+                        newop = SpaceOperation("setfield",
+                                     [newvarsmap[key], c_name, op.args[2]],
+                                               op.result)
                         newops.append(newop)
-                        last_removed_access = len(newops)
-                    elif op.opname in ("setfield", "setarrayitem"):
-                        S = op.args[0].concretetype.TO
-                        fldname = op.args[1].value
-                        key = key_for_field_access(S, fldname)
-                        assert key in newvarsmap
-                        if key in accessed_substructs:
-                            c_name = Constant('data', lltype.Void)
-                            newop = SpaceOperation("setfield",
-                                         [newvarsmap[key], c_name, op.args[2]],
-                                                   op.result)
-                            newops.append(newop)
-                        else:
-                            newvarsmap[key] = op.args[2]
-                        last_removed_access = len(newops)
-                    elif op.opname in ("same_as", "cast_pointer"):
+                    else:
+                        newvarsmap[key] = op.args[2]
+                    last_removed_access = len(newops)
+                elif op.opname in ("same_as", "cast_pointer"):
+                    assert op.result not in vars
+                    vars[op.result] = True
+                    # Consider the two pointers (input and result) as
+                    # equivalent.  We can, and indeed must, use the same
+                    # flattened list of variables for both, as a "setfield"
+                    # via one pointer must be reflected in the other.
+                elif op.opname == 'keepalive':
+                    last_removed_access = len(newops)
+                elif op.opname in ("getsubstruct", "getarraysubstruct",
+                                   "direct_fieldptr"):
+                    S = op.args[0].concretetype.TO
+                    fldname = op.args[1].value
+                    if op.opname == "getarraysubstruct":
+                        fldname = 'item%d' % fldname
+                    equiv = self.equivalent_substruct(S, fldname)
+                    if equiv:
+                        # exactly like a cast_pointer
                         assert op.result not in vars
                         vars[op.result] = True
-                        # Consider the two pointers (input and result) as
-                        # equivalent.  We can, and indeed must, use the same
-                        # flattened list of variables for both, as a "setfield"
-                        # via one pointer must be reflected in the other.
-                    elif op.opname == 'keepalive':
-                        last_removed_access = len(newops)
-                    elif op.opname in ("getsubstruct", "getarraysubstruct",
-                                       "direct_fieldptr"):
-                        S = op.args[0].concretetype.TO
-                        fldname = op.args[1].value
-                        if op.opname == "getarraysubstruct":
-                            fldname = 'item%d' % fldname
-                        equiv = equivalent_substruct(S, fldname)
-                        if equiv:
-                            # exactly like a cast_pointer
-                            assert op.result not in vars
-                            vars[op.result] = True
-                        else:
-                            # do it with a getsubstruct on the independently
-                            # malloc'ed GcStruct
-                            if op.opname == "direct_fieldptr":
-                                opname = "direct_fieldptr"
-                            else:
-                                opname = "getsubstruct"
-                            v = newvarsmap[S, fldname]
-                            cname = Constant('data', lltype.Void)
-                            newop = SpaceOperation(opname,
-                                                   [v, cname],
-                                                   op.result)
-                            newops.append(newop)
-                    elif op.opname in ("ptr_iszero", "ptr_nonzero"):
-                        # we know the pointer is not NULL if it comes from
-                        # a successful malloc
-                        c = Constant(op.opname == "ptr_nonzero", lltype.Bool)
-                        newop = SpaceOperation('same_as', [c], op.result)
-                        newops.append(newop)
                     else:
-                        raise AssertionError, op.opname
-                elif op.result in vars:
-                    assert op.opname == "malloc"
-                    assert vars == {var: True}
-                    progress = True
-                    # drop the "malloc" operation
-                    newvarsmap = flatconstants.copy()   # zero initial values
-                    # if there are substructures, they are now individually
-                    # malloc'ed in an exploded way.  (They will typically be
-                    # removed again by the next malloc removal pass.)
-                    for key in needsubmallocs:
-                        v = Variable()
-                        v.concretetype = newvarstype[key]
-                        c = Constant(v.concretetype.TO, lltype.Void)
-                        if c.value == op.args[0].value:
-                            progress = False   # replacing a malloc with
-                                               # the same malloc!
-                        newop = SpaceOperation("malloc", [c], v)
+                        # do it with a getsubstruct on the independently
+                        # malloc'ed GcStruct
+                        if op.opname == "direct_fieldptr":
+                            opname = "direct_fieldptr"
+                        else:
+                            opname = "getsubstruct"
+                        v = newvarsmap[S, fldname]
+                        cname = Constant('data', lltype.Void)
+                        newop = SpaceOperation(opname,
+                                               [v, cname],
+                                               op.result)
                         newops.append(newop)
-                        newvarsmap[key] = v
-                    count[0] += progress
+                elif op.opname in ("ptr_iszero", "ptr_nonzero"):
+                    # we know the pointer is not NULL if it comes from
+                    # a successful malloc
+                    c = Constant(op.opname == "ptr_nonzero", lltype.Bool)
+                    newop = SpaceOperation('same_as', [c], op.result)
+                    newops.append(newop)
                 else:
-                    newops.append(op)
+                    raise AssertionError, op.opname
+            elif op.result in vars:
+                assert op.opname == self.MALLOC_OP
+                assert vars == {var: True}
+                progress = True
+                # drop the "malloc" operation
+                newvarsmap = self.flatconstants.copy()   # zero initial values
+                # if there are substructures, they are now individually
+                # malloc'ed in an exploded way.  (They will typically be
+                # removed again by the next malloc removal pass.)
+                for key in self.needsubmallocs:
+                    v = Variable()
+                    v.concretetype = self.newvarstype[key]
+                    c = Constant(v.concretetype.TO, lltype.Void)
+                    if c.value == op.args[0].value:
+                        progress = False   # replacing a malloc with
+                                           # the same malloc!
+                    newop = SpaceOperation(self.MALLOC_OP, [c], v)
+                    newops.append(newop)
+                    newvarsmap[key] = v
+                count[0] += progress
+            else:
+                newops.append(op)
 
-            assert block.exitswitch not in vars
+        assert block.exitswitch not in vars
 
-            for link in block.exits:
-                newargs = []
-                for arg in link.args:
-                    if arg in vars:
-                        newargs += list_newvars()
-                    else:
-                        newargs.append(arg)
-                link.args[:] = newargs
-
-            if last_removed_access is not None:
-                keepalives = []
-                for v in list_newvars():
-                    T = v.concretetype
-                    if isinstance(T, lltype.Ptr) and T._needsgc():
-                        v0 = Variable()
-                        v0.concretetype = lltype.Void
-                        newop = SpaceOperation('keepalive', [v], v0)
-                        keepalives.append(newop)
-                newops[last_removed_access:last_removed_access] = keepalives
-
-            block.operations[:] = newops
-
-        # look for variables arriving from outside the block
-        for var in vars:
-            if var in block.inputargs:
-                i = block.inputargs.index(var)
-                newinputargs = block.inputargs[:i]
-                newvarsmap = {}
-                for key in flatnames:
-                    newvar = Variable()
-                    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)
+        for link in block.exits:
+            newargs = []
+            for arg in link.args:
+                if arg in vars:
+                    newargs += list_newvars()
+                else:
+                    newargs.append(arg)
+            link.args[:] = newargs
 
-        # look for variables created inside the block by a malloc
-        vars_created_here = []
-        for op in block.operations:
-            if op.opname == "malloc" and op.result in vars:
-                vars_created_here.append(op.result)
-        for var in vars_created_here:
-            flowin(var, newvarsmap=None)
-
-    return count[0]
-
-def remove_mallocs_once(graph):
-    """Perform one iteration of malloc removal."""
-    remove_identical_vars(graph)
-    lifetimes = compute_lifetimes(graph)
-    progress = 0
-    for info in lifetimes:
-        progress += _try_inline_malloc(info)
-    return progress
-
-def remove_simple_mallocs(graph):
-    """Iteratively remove (inline) the mallocs that can be simplified away."""
-    tot = 0
-    while True:
-        count = remove_mallocs_once(graph)
-        if count:
-            log.malloc('%d simple mallocs removed in %r' % (count, graph.name))
-            tot += count
-        else:
-            break
-    return tot
+        if last_removed_access is not None:
+            keepalives = []
+            for v in list_newvars():
+                T = v.concretetype
+                if isinstance(T, lltype.Ptr) and T._needsgc():
+                    v0 = Variable()
+                    v0.concretetype = lltype.Void
+                    newop = SpaceOperation('keepalive', [v], v0)
+                    keepalives.append(newop)
+            newops[last_removed_access:last_removed_access] = keepalives
+
+        block.operations[:] = newops
+
+class OOTypeMallocRemover(BaseMallocRemover):
+    pass # TODO
+
+def remove_simple_mallocs(graph, type_system='lltype'):
+    if type_system == 'lltype':
+        remover = LLTypeMallocRemover()
+    else:
+        remover = OOTypeMallocRemover()
+    return remover.remove_simple_mallocs(graph)

Modified: pypy/dist/pypy/translator/backendopt/test/test_malloc.py
==============================================================================
--- pypy/dist/pypy/translator/backendopt/test/test_malloc.py	(original)
+++ pypy/dist/pypy/translator/backendopt/test/test_malloc.py	Fri Dec 29 14:34:40 2006
@@ -1,6 +1,5 @@
 import py
-from pypy.translator.backendopt.malloc import remove_mallocs_once
-from pypy.translator.backendopt.malloc import union_wrapper
+from pypy.translator.backendopt.malloc import LLTypeMallocRemover
 from pypy.translator.backendopt.inline import inline_function
 from pypy.translator.backendopt.all import backend_optimizations
 from pypy.translator.translator import TranslationContext, graphof
@@ -13,6 +12,7 @@
 
 
 def check_malloc_removed(graph):
+    remover = LLTypeMallocRemover()
     checkgraph(graph)
     count1 = count2 = 0
     for node in flatten(graph):
@@ -20,7 +20,7 @@
             for op in node.operations:
                 if op.opname == 'malloc':
                     S = op.args[0].value
-                    if not union_wrapper(S):   # union wrappers are fine
+                    if not remover.union_wrapper(S):   # union wrappers are fine
                         count1 += 1
                 if op.opname in ('direct_call', 'indirect_call'):
                     count2 += 1
@@ -28,6 +28,7 @@
     assert count2 == 0   # number of calls left
 
 def check(fn, signature, args, expected_result, must_be_removed=True):
+    remover = LLTypeMallocRemover()
     t = TranslationContext()
     t.buildannotator().build_types(fn, signature)
     t.buildrtyper().specialize()
@@ -37,7 +38,7 @@
     # to detect missing keepalives and broken intermediate graphs,
     # we do the loop ourselves instead of calling remove_simple_mallocs()
     while True:
-        progress = remove_mallocs_once(graph)
+        progress = remover.remove_mallocs_once(graph)
         simplify.transform_dead_op_vars_in_blocks(list(graph.iterblocks()))
         if progress and option.view:
             t.view()



More information about the Pypy-commit mailing list