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

pedronis at codespeak.net pedronis at codespeak.net
Sun Dec 31 02:12:00 CET 2006


Author: pedronis
Date: Sun Dec 31 02:11:54 2006
New Revision: 36068

Modified:
   pypy/dist/pypy/translator/backendopt/all.py
   pypy/dist/pypy/translator/backendopt/malloc.py
   pypy/dist/pypy/translator/backendopt/test/test_malloc.py
Log:
umph, reverting the ootype malloc removal refactoring 36037 (and consequently 36040), it needs more careful review.

It broke in a shallow way backendopt/test/test_all.py but once the shallow breakage is repaired we see that
a bunch of tests there are failing because malloc removal is not happening anymore it seems.

This needs to be fixed in such way that test_all and translator/test/test_simplify pass again and then it can be
checked in again.

test_all is quite an important test for *all* backendopts!



Modified: pypy/dist/pypy/translator/backendopt/all.py
==============================================================================
--- pypy/dist/pypy/translator/backendopt/all.py	(original)
+++ pypy/dist/pypy/translator/backendopt/all.py	Sun Dec 31 02:11:54 2006
@@ -92,7 +92,6 @@
 def inline_malloc_removal_phase(config, translator, graphs, inline_threshold,
                                 call_count_pred=None):
 
-    type_system = translator.rtyper.type_system.name
     log.inlining("phase with threshold factor: %s" % inline_threshold)
 
     # inline functions in each other
@@ -114,7 +113,7 @@
     if config.mallocs:
         tot = 0
         for graph in graphs:
-            count = remove_simple_mallocs(graph, type_system)
+            count = remove_simple_mallocs(graph)
             if count:
                 # remove typical leftovers from malloc removal
                 removenoops.remove_same_as(graph)

Modified: pypy/dist/pypy/translator/backendopt/malloc.py
==============================================================================
--- pypy/dist/pypy/translator/backendopt/malloc.py	(original)
+++ pypy/dist/pypy/translator/backendopt/malloc.py	Sun Dec 31 02:11:54 2006
@@ -2,7 +2,6 @@
 from pypy.objspace.flow.model import SpaceOperation, traverse
 from pypy.tool.algo.unionfind import UnionFind
 from pypy.rpython.lltypesystem import lltype
-from pypy.rpython.ootypesystem import ootype
 from pypy.translator.simplify import remove_identical_vars
 from pypy.translator.backendopt.support import log
 
@@ -50,58 +49,7 @@
         raise NotImplementedError
 
     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}
-        self.last_removed_access = None
-
-        def list_newvars():
-            return [newvarsmap[key] for key in self.flatnames]
-
-        assert block.operations != ()
-        self.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:
-                self.flowin_op(op, vars, newvarsmap)
-            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)
-                    self.newops.append(newop)
-                    newvarsmap[key] = v
-                count[0] += progress
-            else:
-                self.newops.append(op)
-
-        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
-
-        self.insert_keepalives(list_newvars())
-        block.operations[:] = self.newops
+        raise NotImplementedError
 
     def compute_lifetimes(self, graph):
         """Compute the static data flow of the graph: returns a list of LifeTime
@@ -419,170 +367,141 @@
                 pass
         return S, fldname
 
-    def flowin_op(self, op, vars, newvarsmap):
-        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)
-            self.newops.append(newop)
-            self.last_removed_access = len(self.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)
-                self.newops.append(newop)
-            else:
-                newvarsmap[key] = op.args[2]
-                self.last_removed_access = len(self.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':
-            self.last_removed_access = len(self.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
+    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 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 = 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)
+                    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
+                    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 == 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:
-                # do it with a getsubstruct on the independently
-                # malloc'ed GcStruct
-                if op.opname == "direct_fieldptr":
-                    opname = "direct_fieldptr"
+                newops.append(op)
+
+        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:
-                    opname = "getsubstruct"
-                v = newvarsmap[S, fldname]
-                cname = Constant('data', lltype.Void)
-                newop = SpaceOperation(opname,
-                                       [v, cname],
-                                       op.result)
-                self.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)
-            self.newops.append(newop)
-        else:
-            raise AssertionError, op.opname
-
-        
-    def insert_keepalives(self, newvars):
-        if self.last_removed_access is not None:
+                    newargs.append(arg)
+            link.args[:] = newargs
+
+        if last_removed_access is not None:
             keepalives = []
-            for v in newvars:
+            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)
-            self.newops[self.last_removed_access:self.last_removed_access] = keepalives
-
-class OOTypeMallocRemover(BaseMallocRemover):
-
-    IDENTITY_OPS = ('same_as', 'ooupcast', 'oodowncast')
-    SUBSTRUCT_OPS = ()
-    MALLOC_OP = 'new'
-    FIELD_ACCESS = dict.fromkeys(["oogetfield",
-                                  "oosetfield",
-                                  "oononnull",
-                                  #"oois",  # ???
-                                  #"instanceof", # ???
-                                  ])
-    SUBSTRUCT_ACCESS = {}
-    CHECK_ARRAY_INDEX = {}
-
-    def get_STRUCT(self, TYPE):
-        return TYPE
-
-    def union_wrapper(self, S):
-        return False
-
-    def RTTI_dtor(self, STRUCT):
-        return False
+            newops[last_removed_access:last_removed_access] = keepalives
 
-    def _get_fields(self, TYPE):
-        if isinstance(TYPE, ootype.Record):
-            return TYPE._fields
-        elif isinstance(TYPE, ootype.Instance):
-            return TYPE._allfields()
-        else:
-            assert False
-
-    def flatten(self, TYPE):
-        for name, (FIELDTYPE, default) in self._get_fields(TYPE).iteritems():
-            key = TYPE, name
-            example = FIELDTYPE._defl()
-            constant = Constant(example)
-            constant.concretetype = FIELDTYPE
-            self.flatconstants[key] = constant
-            self.flatnames.append(key)
-            self.newvarstype[key] = FIELDTYPE
+        block.operations[:] = newops
 
-    def key_for_field_access(self, S, fldname):
-        return S, fldname
-
-    def flowin_op(self, op, vars, newvarsmap):
-        if op.opname == "oogetfield":
-            S = op.args[0].concretetype
-            fldname = op.args[1].value
-            key = self.key_for_field_access(S, fldname)
-            newop = SpaceOperation("same_as",
-                                   [newvarsmap[key]],
-                                   op.result)
-            self.newops.append(newop)
-            last_removed_access = len(self.newops)
-        elif op.opname == "oosetfield":
-            S = op.args[0].concretetype
-            fldname = op.args[1].value
-            key = self.key_for_field_access(S, fldname)
-            assert key in newvarsmap
-            newvarsmap[key] = op.args[2]
-            last_removed_access = len(self.newops)
-        elif op.opname in ("same_as", "oodowncast", "ooupcast"):
-            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 == "oononnull":
-            # we know the pointer is not NULL if it comes from
-            # a successful malloc
-            c = Constant(True, lltype.Bool)
-            newop = SpaceOperation('same_as', [c], op.result)
-            self.newops.append(newop)
-        else:
-            raise AssertionError, op.opname
-
-    def insert_keepalives(self, newvars):
-        pass
+class OOTypeMallocRemover(BaseMallocRemover):
+    pass # TODO
 
 def remove_simple_mallocs(graph, type_system='lltype'):
     if type_system == 'lltype':

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	Sun Dec 31 02:11:54 2006
@@ -1,5 +1,5 @@
 import py
-from pypy.translator.backendopt.malloc import LLTypeMallocRemover, OOTypeMallocRemover
+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
@@ -7,336 +7,311 @@
 from pypy.objspace.flow.model import checkgraph, flatten, Block, mkentrymap
 from pypy.rpython.llinterp import LLInterpreter
 from pypy.rpython.lltypesystem import lltype, llmemory
-from pypy.rpython.ootypesystem import ootype
 from pypy.rlib import objectmodel
 from pypy.conftest import option
 
-class BaseMallocRemovalTest(object):
-    type_system = None
-    MallocRemover = None
-
-    def _skip_oo(self, msg):
-        if self.type_system == 'ootype':
-            py.test.skip(msg)
-
-    def check_malloc_removed(self, graph):
-        remover = self.MallocRemover()
-        checkgraph(graph)
-        count1 = count2 = 0
-        for node in flatten(graph):
-            if isinstance(node, Block):
-                for op in node.operations:
-                    if op.opname == self.MallocRemover.MALLOC_OP:
-                        S = op.args[0].value
-                        if not remover.union_wrapper(S):   # union wrappers are fine
-                            count1 += 1
-                    if op.opname in ('direct_call', 'indirect_call'):
-                        count2 += 1
-        assert count1 == 0   # number of mallocs left
-        assert count2 == 0   # number of calls left
-
-    def check(self, fn, signature, args, expected_result, must_be_removed=True):
-        remover = self.MallocRemover()
-        t = TranslationContext()
-        t.buildannotator().build_types(fn, signature)
-        t.buildrtyper(type_system=self.type_system).specialize()
-        graph = graphof(t, fn)
-        if option.view:
-            t.view()
-        # to detect missing keepalives and broken intermediate graphs,
-        # we do the loop ourselves instead of calling remove_simple_mallocs()
-        while True:
-            progress = remover.remove_mallocs_once(graph)
-            simplify.transform_dead_op_vars_in_blocks(list(graph.iterblocks()))
-            if progress and option.view:
-                t.view()
-            if expected_result is not Ellipsis:
-                interp = LLInterpreter(t.rtyper)
-                res = interp.eval_graph(graph, args)
-                assert res == expected_result
-            if not progress:
-                break
-        if must_be_removed:
-            self.check_malloc_removed(graph)
-        return graph
-
-    def test_fn1(self):
-        def fn1(x, y):
-            if x > 0:
-                t = x+y, x-y
-            else:
-                t = x-y, x+y
-            s, d = t
-            return s*d
-        self.check(fn1, [int, int], [15, 10], 125)
-
-    def test_fn2(self):
-        class T:
-            pass
-        def fn2(x, y):
-            t = T()
-            t.x = x
-            t.y = y
-            if x > 0:
-                return t.x + t.y
-            else:
-                return t.x - t.y
-        self.check(fn2, [int, int], [-6, 7], -13)
-
-    def test_fn3(self):
-        def fn3(x):
-            a, ((b, c), d, e) = x+1, ((x+2, x+3), x+4, x+5)
-            return a+b+c+d+e
-        self.check(fn3, [int], [10], 65)
-
-    def test_fn4(self):
-        class A:
-            pass
-        class B(A):
-            pass
-        def fn4(i):
-            a = A()
-            b = B()
-            a.b = b
-            b.i = i
-            return a.b.i
-        self.check(fn4, [int], [42], 42)
-
-    def test_fn5(self):
-        self._skip_oo('It will work as soon as trivial oosend are inlined')
-        class A:
-            attr = 666
-        class B(A):
-            attr = 42
-        def fn5():
-            b = B()
-            return b.attr
-        self.check(fn5, [], [], 42)
-
-    def test_aliasing(self):
-        class A:
-            pass
-        def fn6(n):
-            a1 = A()
-            a1.x = 5
-            a2 = A()
-            a2.x = 6
-            if n > 0:
-                a = a1
-            else:
-                a = a2
-            a.x = 12
-            return a1.x
-        self.check(fn6, [int], [1], 12, must_be_removed=False)
-
-
-
-class TestLLTypeMallocRemoval(BaseMallocRemovalTest):
-    type_system = 'lltype'
-    MallocRemover = LLTypeMallocRemover
-
-    def test_with_keepalive(self):
-        from pypy.rlib.objectmodel import keepalive_until_here
-        def fn1(x, y):
-            if x > 0:
-                t = x+y, x-y
-            else:
-                t = x-y, x+y
-            s, d = t
-            keepalive_until_here(t)
-            return s*d
-        self.check(fn1, [int, int], [15, 10], 125)
-
-    def test_dont_remove_with__del__(self):
-        import os
-        delcalls = [0]
-        class A(object):
-            nextid = 0
-            def __init__(self):
-                self.id = self.nextid
-                self.nextid += 1
-
-            def __del__(self):
-                delcalls[0] += 1
-                os.write(1, "__del__\n")
 
-        def f(x=int):
-            a = A()
-            i = 0
-            while i < x:
-                a = A()
-                os.write(1, str(delcalls[0]) + "\n")
-                i += 1
-            return 1
-        t = TranslationContext()
-        t.buildannotator().build_types(f, [int])
-        t.buildrtyper().specialize()
-        graph = graphof(t, f)
-        backend_optimizations(t)
-        op = graph.startblock.exits[0].target.exits[1].target.operations[0]
-        assert op.opname == "malloc"
-
-    def test_add_keepalives(self):
-        class A:
-            pass
-        SMALL = lltype.Struct('SMALL', ('x', lltype.Signed))
-        BIG = lltype.GcStruct('BIG', ('z', lltype.Signed), ('s', SMALL))
-        def fn7(i):
-            big = lltype.malloc(BIG)
+def check_malloc_removed(graph):
+    remover = LLTypeMallocRemover()
+    checkgraph(graph)
+    count1 = count2 = 0
+    for node in flatten(graph):
+        if isinstance(node, Block):
+            for op in node.operations:
+                if op.opname == 'malloc':
+                    S = op.args[0].value
+                    if not remover.union_wrapper(S):   # union wrappers are fine
+                        count1 += 1
+                if op.opname in ('direct_call', 'indirect_call'):
+                    count2 += 1
+    assert count1 == 0   # number of mallocs left
+    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()
+    graph = graphof(t, fn)
+    if option.view:
+        t.view()
+    # to detect missing keepalives and broken intermediate graphs,
+    # we do the loop ourselves instead of calling remove_simple_mallocs()
+    while True:
+        progress = remover.remove_mallocs_once(graph)
+        simplify.transform_dead_op_vars_in_blocks(list(graph.iterblocks()))
+        if progress and option.view:
+            t.view()
+        if expected_result is not Ellipsis:
+            interp = LLInterpreter(t.rtyper)
+            res = interp.eval_graph(graph, args)
+            assert res == expected_result
+        if not progress:
+            break
+    if must_be_removed:
+        check_malloc_removed(graph)
+    return graph
+
+
+def test_fn1():
+    def fn1(x, y):
+        if x > 0:
+            t = x+y, x-y
+        else:
+            t = x-y, x+y
+        s, d = t
+        return s*d
+    check(fn1, [int, int], [15, 10], 125)
+
+def test_fn2():
+    class T:
+        pass
+    def fn2(x, y):
+        t = T()
+        t.x = x
+        t.y = y
+        if x > 0:
+            return t.x + t.y
+        else:
+            return t.x - t.y
+    check(fn2, [int, int], [-6, 7], -13)
+
+def test_fn3():
+    def fn3(x):
+        a, ((b, c), d, e) = x+1, ((x+2, x+3), x+4, x+5)
+        return a+b+c+d+e
+    check(fn3, [int], [10], 65)
+
+def test_fn4():
+    class A:
+        pass
+    class B(A):
+        pass
+    def fn4(i):
+        a = A()
+        b = B()
+        a.b = b
+        b.i = i
+        return a.b.i
+    check(fn4, [int], [42], 42)
+
+def test_fn5():
+    class A:
+        attr = 666
+    class B(A):
+        attr = 42
+    def fn5():
+        b = B()
+        return b.attr
+    check(fn5, [], [], 42)
+
+def test_aliasing():
+    class A:
+        pass
+    def fn6(n):
+        a1 = A()
+        a1.x = 5
+        a2 = A()
+        a2.x = 6
+        if n > 0:
+            a = a1
+        else:
+            a = a2
+        a.x = 12
+        return a1.x
+    check(fn6, [int], [1], 12, must_be_removed=False)
+
+def test_with_keepalive():
+    from pypy.rlib.objectmodel import keepalive_until_here
+    def fn1(x, y):
+        if x > 0:
+            t = x+y, x-y
+        else:
+            t = x-y, x+y
+        s, d = t
+        keepalive_until_here(t)
+        return s*d
+    check(fn1, [int, int], [15, 10], 125)
+
+def test_dont_remove_with__del__():
+    import os
+    delcalls = [0]
+    class A(object):
+        nextid = 0
+        def __init__(self):
+            self.id = self.nextid
+            self.nextid += 1
+
+        def __del__(self):
+            delcalls[0] += 1
+            os.write(1, "__del__\n")
+
+    def f(x=int):
+        a = A()
+        i = 0
+        while i < x:
             a = A()
-            a.big = big
-            a.small = big.s
-            a.small.x = 0
-            while i > 0:
-                a.small.x += i
-                i -= 1
-            return a.small.x
-        self.check(fn7, [int], [10], 55, must_be_removed=False)
-
-    def test_getsubstruct(self):
-        SMALL = lltype.Struct('SMALL', ('x', lltype.Signed))
-        BIG = lltype.GcStruct('BIG', ('z', lltype.Signed), ('s', SMALL))
-
-        def fn(n1, n2):
-            b = lltype.malloc(BIG)
-            b.z = n1
-            b.s.x = n2
-            return b.z - b.s.x
-
-        self.check(fn, [int, int], [100, 58], 42)
-
-    def test_fixedsizearray(self):
-        A = lltype.FixedSizeArray(lltype.Signed, 3)
-        S = lltype.GcStruct('S', ('a', A))
-
-        def fn(n1, n2):
-            s = lltype.malloc(S)
-            a = s.a
-            a[0] = n1
-            a[2] = n2
-            return a[0]-a[2]
-
-        self.check(fn, [int, int], [100, 42], 58)
-
-    def test_wrapper_cannot_be_removed(self):
-        SMALL = lltype.OpaqueType('SMALL')
-        BIG = lltype.GcStruct('BIG', ('z', lltype.Signed), ('s', SMALL))
-
-        def g(small):
-            return -1
-        def fn():
-            b = lltype.malloc(BIG)
-            g(b.s)
-
-        self.check(fn, [], [], None, must_be_removed=False)
-
-    def test_direct_fieldptr(self):
-        S = lltype.GcStruct('S', ('x', lltype.Signed))
-
-        def fn():
-            s = lltype.malloc(S)
-            s.x = 11
-            p = lltype.direct_fieldptr(s, 'x')
-            return p[0]
-
-        self.check(fn, [], [], 11)
-
-    def test_direct_fieldptr_2(self):
-        T = lltype.GcStruct('T', ('z', lltype.Signed))
-        S = lltype.GcStruct('S', ('t', T),
-                                 ('x', lltype.Signed),
-                                 ('y', lltype.Signed))
-        def fn():
-            s = lltype.malloc(S)
-            s.x = 10
-            s.t.z = 1
-            px = lltype.direct_fieldptr(s, 'x')
-            py = lltype.direct_fieldptr(s, 'y')
-            pz = lltype.direct_fieldptr(s.t, 'z')
-            py[0] = 31
-            return px[0] + s.y + pz[0]
-
-        self.check(fn, [], [], 42)
-
-    def test_getarraysubstruct(self):
-        U = lltype.Struct('U', ('n', lltype.Signed))
-        for length in [1, 2]:
-            S = lltype.GcStruct('S', ('a', lltype.FixedSizeArray(U, length)))
-            for index in range(length):
-
-                def fn():
-                    s = lltype.malloc(S)
-                    s.a[index].n = 12
-                    return s.a[index].n
-                self.check(fn, [], [], 12)
-
-    def test_ptr_nonzero(self):
-        S = lltype.GcStruct('S')
-        def fn():
-            s = lltype.malloc(S)
-            return bool(s)
-        self.check(fn, [], [], True)
-
-    def test_substruct_not_accessed(self):
-        SMALL = lltype.Struct('SMALL', ('x', lltype.Signed))
-        BIG = lltype.GcStruct('BIG', ('z', lltype.Signed), ('s', SMALL))
-        def fn():
-            x = lltype.malloc(BIG)
-            while x.z < 10:    # makes several blocks
-                x.z += 3
-            return x.z
-        self.check(fn, [], [], 12)
-
-    def test_union(self):
-        UNION = lltype.Struct('UNION', ('a', lltype.Signed), ('b', lltype.Signed),
-                              hints = {'union': True})
-        BIG = lltype.GcStruct('BIG', ('u1', UNION), ('u2', UNION))
-        def fn():
-            x = lltype.malloc(BIG)
-            x.u1.a = 3
-            x.u2.b = 6
-            return x.u1.b * x.u2.a
-        self.check(fn, [], [], Ellipsis)
-
-    def test_keep_all_keepalives(self):
-        SIZE = llmemory.sizeof(lltype.Signed)
-        PARRAY = lltype.Ptr(lltype.FixedSizeArray(lltype.Signed, 1))
-        class A:
-            def __init__(self):
-                self.addr = llmemory.raw_malloc(SIZE)
-            def __del__(self):
-                llmemory.raw_free(self.addr)
-        class B:
-            pass
-        def myfunc():
-            b = B()
-            b.keep = A()
-            b.data = llmemory.cast_adr_to_ptr(b.keep.addr, PARRAY)
-            b.data[0] = 42
-            ptr = b.data
-            # normally 'b' could go away as early as here, which would free
-            # the memory held by the instance of A in b.keep...
-            res = ptr[0]
-            # ...so we explicitly keep 'b' alive until here
-            objectmodel.keepalive_until_here(b)
-            return res
-        graph = self.check(myfunc, [], [], 42,
-                           must_be_removed=False)    # 'A' instance left
-
-        # there is a getarrayitem near the end of the graph of myfunc.
-        # However, the memory it accesses must still be protected by the
-        # following keepalive, even after malloc removal
-        entrymap = mkentrymap(graph)
-        [link] = entrymap[graph.returnblock]
-        assert link.prevblock.operations[-1].opname == 'keepalive'
-
-class TestOOTypeMallocRemoval(BaseMallocRemovalTest):
-    type_system = 'ootype'
-    MallocRemover = OOTypeMallocRemover
-
-    def test_oononnull(self):
-        FOO = ootype.Instance('Foo', ootype.ROOT)
-        def fn():
-            s = ootype.new(FOO)
-            return bool(s)
-        self.check(fn, [], [], True)
+            os.write(1, str(delcalls[0]) + "\n")
+            i += 1
+        return 1
+    t = TranslationContext()
+    t.buildannotator().build_types(f, [int])
+    t.buildrtyper().specialize()
+    graph = graphof(t, f)
+    backend_optimizations(t)
+    op = graph.startblock.exits[0].target.exits[1].target.operations[0]
+    assert op.opname == "malloc"
+
+def test_add_keepalives():
+    class A:
+        pass
+    SMALL = lltype.Struct('SMALL', ('x', lltype.Signed))
+    BIG = lltype.GcStruct('BIG', ('z', lltype.Signed), ('s', SMALL))
+    def fn7(i):
+        big = lltype.malloc(BIG)
+        a = A()
+        a.big = big
+        a.small = big.s
+        a.small.x = 0
+        while i > 0:
+            a.small.x += i
+            i -= 1
+        return a.small.x
+    check(fn7, [int], [10], 55, must_be_removed=False)
+
+def test_getsubstruct():
+    SMALL = lltype.Struct('SMALL', ('x', lltype.Signed))
+    BIG = lltype.GcStruct('BIG', ('z', lltype.Signed), ('s', SMALL))
+
+    def fn(n1, n2):
+        b = lltype.malloc(BIG)
+        b.z = n1
+        b.s.x = n2
+        return b.z - b.s.x
+
+    check(fn, [int, int], [100, 58], 42)
+
+def test_fixedsizearray():
+    A = lltype.FixedSizeArray(lltype.Signed, 3)
+    S = lltype.GcStruct('S', ('a', A))
+
+    def fn(n1, n2):
+        s = lltype.malloc(S)
+        a = s.a
+        a[0] = n1
+        a[2] = n2
+        return a[0]-a[2]
+
+    check(fn, [int, int], [100, 42], 58)
+
+def test_wrapper_cannot_be_removed():
+    SMALL = lltype.OpaqueType('SMALL')
+    BIG = lltype.GcStruct('BIG', ('z', lltype.Signed), ('s', SMALL))
+
+    def g(small):
+        return -1
+    def fn():
+        b = lltype.malloc(BIG)
+        g(b.s)
+
+    check(fn, [], [], None, must_be_removed=False)
+
+def test_direct_fieldptr():
+    S = lltype.GcStruct('S', ('x', lltype.Signed))
+
+    def fn():
+        s = lltype.malloc(S)
+        s.x = 11
+        p = lltype.direct_fieldptr(s, 'x')
+        return p[0]
+
+    check(fn, [], [], 11)
+
+def test_direct_fieldptr_2():
+    T = lltype.GcStruct('T', ('z', lltype.Signed))
+    S = lltype.GcStruct('S', ('t', T),
+                             ('x', lltype.Signed),
+                             ('y', lltype.Signed))
+    def fn():
+        s = lltype.malloc(S)
+        s.x = 10
+        s.t.z = 1
+        px = lltype.direct_fieldptr(s, 'x')
+        py = lltype.direct_fieldptr(s, 'y')
+        pz = lltype.direct_fieldptr(s.t, 'z')
+        py[0] = 31
+        return px[0] + s.y + pz[0]
+
+    check(fn, [], [], 42)
+
+def test_getarraysubstruct():
+    U = lltype.Struct('U', ('n', lltype.Signed))
+    for length in [1, 2]:
+        S = lltype.GcStruct('S', ('a', lltype.FixedSizeArray(U, length)))
+        for index in range(length):
+
+            def fn():
+                s = lltype.malloc(S)
+                s.a[index].n = 12
+                return s.a[index].n
+            check(fn, [], [], 12)
+
+def test_ptr_nonzero():
+    S = lltype.GcStruct('S')
+    def fn():
+        s = lltype.malloc(S)
+        return bool(s)
+    check(fn, [], [], True)
+
+def test_substruct_not_accessed():
+    SMALL = lltype.Struct('SMALL', ('x', lltype.Signed))
+    BIG = lltype.GcStruct('BIG', ('z', lltype.Signed), ('s', SMALL))
+    def fn():
+        x = lltype.malloc(BIG)
+        while x.z < 10:    # makes several blocks
+            x.z += 3
+        return x.z
+    check(fn, [], [], 12)
+
+def test_union():
+    UNION = lltype.Struct('UNION', ('a', lltype.Signed), ('b', lltype.Signed),
+                          hints = {'union': True})
+    BIG = lltype.GcStruct('BIG', ('u1', UNION), ('u2', UNION))
+    def fn():
+        x = lltype.malloc(BIG)
+        x.u1.a = 3
+        x.u2.b = 6
+        return x.u1.b * x.u2.a
+    check(fn, [], [], Ellipsis)
+
+def test_keep_all_keepalives():
+    SIZE = llmemory.sizeof(lltype.Signed)
+    PARRAY = lltype.Ptr(lltype.FixedSizeArray(lltype.Signed, 1))
+    class A:
+        def __init__(self):
+            self.addr = llmemory.raw_malloc(SIZE)
+        def __del__(self):
+            llmemory.raw_free(self.addr)
+    class B:
+        pass
+    def myfunc():
+        b = B()
+        b.keep = A()
+        b.data = llmemory.cast_adr_to_ptr(b.keep.addr, PARRAY)
+        b.data[0] = 42
+        ptr = b.data
+        # normally 'b' could go away as early as here, which would free
+        # the memory held by the instance of A in b.keep...
+        res = ptr[0]
+        # ...so we explicitly keep 'b' alive until here
+        objectmodel.keepalive_until_here(b)
+        return res
+    graph = check(myfunc, [], [], 42,
+                  must_be_removed=False)    # 'A' instance left
+
+    # there is a getarrayitem near the end of the graph of myfunc.
+    # However, the memory it accesses must still be protected by the
+    # following keepalive, even after malloc removal
+    entrymap = mkentrymap(graph)
+    [link] = entrymap[graph.returnblock]
+    assert link.prevblock.operations[-1].opname == 'keepalive'



More information about the Pypy-commit mailing list