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

antocuni at codespeak.net antocuni at codespeak.net
Fri Dec 29 16:45:23 CET 2006


Author: antocuni
Date: Fri Dec 29 16:45:19 2006
New Revision: 36037

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:
Malloc removal works with ootype now!



Modified: pypy/dist/pypy/translator/backendopt/all.py
==============================================================================
--- pypy/dist/pypy/translator/backendopt/all.py	(original)
+++ pypy/dist/pypy/translator/backendopt/all.py	Fri Dec 29 16:45:19 2006
@@ -92,6 +92,7 @@
 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
@@ -113,7 +114,7 @@
     if config.mallocs:
         tot = 0
         for graph in graphs:
-            count = remove_simple_mallocs(graph)
+            count = remove_simple_mallocs(graph, type_system)
             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	Fri Dec 29 16:45:19 2006
@@ -2,6 +2,7 @@
 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
 
@@ -49,7 +50,58 @@
         raise NotImplementedError
 
     def flowin(self, block, count, var, newvarsmap):
-        raise NotImplementedError
+        # 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
 
     def compute_lifetimes(self, graph):
         """Compute the static data flow of the graph: returns a list of LifeTime
@@ -367,141 +419,170 @@
                 pass
         return S, fldname
 
-    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
+    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:
-                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()
+                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
+            else:
+                # do it with a getsubstruct on the independently
+                # malloc'ed GcStruct
+                if op.opname == "direct_fieldptr":
+                    opname = "direct_fieldptr"
                 else:
-                    newargs.append(arg)
-            link.args[:] = newargs
-
-        if last_removed_access is not None:
+                    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:
             keepalives = []
-            for v in list_newvars():
+            for v in 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
+            self.newops[self.last_removed_access:self.last_removed_access] = keepalives
 
 class OOTypeMallocRemover(BaseMallocRemover):
-    pass # TODO
+
+    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
+
+    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
+
+    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
 
 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	Fri Dec 29 16:45:19 2006
@@ -1,5 +1,5 @@
 import py
-from pypy.translator.backendopt.malloc import LLTypeMallocRemover
+from pypy.translator.backendopt.malloc import LLTypeMallocRemover, OOTypeMallocRemover
 from pypy.translator.backendopt.inline import inline_function
 from pypy.translator.backendopt.all import backend_optimizations
 from pypy.translator.translator import TranslationContext, graphof
@@ -7,311 +7,336 @@
 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
 
-
-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:
+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()
-        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:
+        # 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)
             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():
-    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'
+            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)



More information about the Pypy-commit mailing list