[pypy-svn] r68746 - in pypy/trunk/pypy/translator/backendopt: . test

cfbolz at codespeak.net cfbolz at codespeak.net
Mon Oct 26 11:25:34 CET 2009


Author: cfbolz
Date: Mon Oct 26 11:25:33 2009
New Revision: 68746

Modified:
   pypy/trunk/pypy/translator/backendopt/all.py
   pypy/trunk/pypy/translator/backendopt/escape.py
   pypy/trunk/pypy/translator/backendopt/test/test_escape.py
Log:
Cleanup escape analysis:
 - kill the useless heap2stack transformation
 - don't track changes to objects, that's a bit useless
 - instead track which objects are returned
 - in turn, make returning not escape an object
 - add a helper that makes it possible to find functions that are "like
   mallocs", i.e. make an object and return it


Modified: pypy/trunk/pypy/translator/backendopt/all.py
==============================================================================
--- pypy/trunk/pypy/translator/backendopt/all.py	(original)
+++ pypy/trunk/pypy/translator/backendopt/all.py	Mon Oct 26 11:25:33 2009
@@ -6,7 +6,6 @@
 from pypy.translator.backendopt.stat import print_statistics
 from pypy.translator.backendopt.merge_if_blocks import merge_if_blocks
 from pypy.translator import simplify
-from pypy.translator.backendopt.escape import malloc_to_stack
 from pypy.translator.backendopt import mallocprediction
 from pypy.translator.backendopt.removeassert import remove_asserts
 from pypy.translator.backendopt.support import log
@@ -123,10 +122,6 @@
                                     call_count_pred=call_count_pred)
     constfold(config, graphs)
 
-    if config.heap2stack:
-        assert graphs is translator.graphs  # XXX for now
-        malloc_to_stack(translator)
-
     if config.merge_if_blocks:
         log.mergeifblocks("starting to merge if blocks")
         for graph in graphs:

Modified: pypy/trunk/pypy/translator/backendopt/escape.py
==============================================================================
--- pypy/trunk/pypy/translator/backendopt/escape.py	(original)
+++ pypy/trunk/pypy/translator/backendopt/escape.py	Mon Oct 26 11:25:33 2009
@@ -1,4 +1,3 @@
-from pypy.annotation.model import setunion
 from pypy.objspace.flow.model import Variable, Constant
 from pypy.rpython.lltypesystem import lltype
 from pypy.translator.simplify import get_graph
@@ -7,37 +6,31 @@
 from pypy.tool.uid import uid
 
 class CreationPoint(object):
-    def __init__(self, creation_method, lltype):
-        self.changes = False
+    def __init__(self, creation_method, TYPE, op=None):
         self.escapes = False
+        self.returns = False
         self.creation_method = creation_method
         if creation_method == "constant":
-            self.changes = True
             self.escapes = True
-            self.malloced = False
-        self.lltype = lltype
+        self.TYPE = TYPE
+        self.op = op
 
     def __repr__(self):
-        return ("CreationPoint(<0x%x>, %r, %s, esc=%s, cha=%s)" %
-                (uid(self), self.lltype, self.creation_method, self.escapes, self.changes))
+        return ("CreationPoint(<0x%x>, %r, %s, esc=%s)" %
+                (uid(self), self.TYPE, self.creation_method, self.escapes))
 
 class VarState(object):
-    def __init__(self, crep=None):
-        self.creation_points = {}
-        if crep is not None:
-            self.creation_points[crep] = True
+    def __init__(self, *creps):
+        self.creation_points = set()
+        for crep in creps:
+            self.creation_points.add(crep)
 
     def contains(self, other):
-        for crep in other.creation_points:
-            if crep not in self.creation_points:
-                return False
-        return True
+        return other.creation_points.issubset(self.creation_points)
 
     def merge(self, other):
-        creation_points = setunion(self.creation_points, other.creation_points)
-        newstate = VarState()
-        newstate.creation_points = creation_points
-        return newstate
+        creation_points = self.creation_points.union(other.creation_points)
+        return VarState(*creation_points)
 
     def setescapes(self):
         changed = []
@@ -47,29 +40,28 @@
                 crep.escapes = True
         return changed
 
-    def setchanges(self):
+    def setreturns(self):
         changed = []
         for crep in self.creation_points:
-            if not crep.changes:
+            if not crep.returns:
                 changed.append(crep)
-                crep.changes = True
+                crep.returns = True
         return changed
-    
+
     def does_escape(self):
         for crep in self.creation_points:
             if crep.escapes:
                 return True
         return False
 
-    def does_change(self):
+    def does_return(self):
         for crep in self.creation_points:
-            if crep.changes:
+            if crep.returns:
                 return True
         return False
-    
+
     def __repr__(self):
-        crepsrepr = (", ".join([repr(crep) for crep in self.creation_points]), )
-        return "VarState({%s})" % crepsrepr
+        return "<VarState %s>" % (self.creation_points, )
 
 class AbstractDataFlowInterpreter(object):
     def __init__(self, translation_context):
@@ -108,15 +100,14 @@
     def setstate(self, var, state):
         self.varstates[var] = state
     
-    def get_creationpoint(self, var, method="?"):
+    def get_creationpoint(self, var, method="?", op=None):
         if var in self.creationpoints:
             return self.creationpoints[var]
-        crep = CreationPoint(method, var.concretetype)
+        crep = CreationPoint(method, var.concretetype, op)
         self.creationpoints[var] = crep
         return crep
     
     def schedule_function(self, graph):
-        #print "scheduling function:", graph.name
         startblock = graph.startblock
         if graph in self.functionargs:
             args = self.functionargs[graph]
@@ -136,54 +127,39 @@
         return resultstate, args
 
     def flow_block(self, block, graph):
-        #print "flowing in block %s of function %s" % (block, graph.name)
         self.flown_blocks[block] = True
         if block is graph.returnblock:
             if isonheap(block.inputargs[0]):
-                changed = self.getstate(block.inputargs[0]).setescapes()
-                self.handle_changed(changed)
+                self.returns(self.getstate(block.inputargs[0]))
             return
         if block is graph.exceptblock:
             if isonheap(block.inputargs[0]):
-                changed = self.getstate(block.inputargs[0]).setescapes()
-                self.handle_changed(changed)
+                self.escapes(self.getstate(block.inputargs[0]))
             if isonheap(block.inputargs[1]):
-                changed = self.getstate(block.inputargs[1]).setescapes()
-                self.handle_changed(changed)
+                self.escapes(self.getstate(block.inputargs[1]))
             return
         self.curr_block = block
         self.curr_graph = graph
-        #print "inputargs", self.getstates(block.inputargs)
         
         for op in block.operations:
             self.flow_operation(op)
-        #print "checking exits..."
         for exit in block.exits:
-            #print "exit", exit
             args = self.getstates(exit.args)
             targetargs = self.getstates(exit.target.inputargs)
-            #print "   newargs", args
-            #print "   targetargs", targetargs
-            # flow every block at least once:
+            # flow every block at least once
             if (multicontains(targetargs, args) and
                 exit.target in self.flown_blocks):
-                #print "   not necessary"
                 continue
-            #else:
-                #print "   scheduling for flowin"
             for prevstate, origstate, var in zip(args, targetargs,
                                                 exit.target.inputargs):
                 if not isonheap(var):
                     continue
                 newstate = prevstate.merge(origstate)
                 self.setstate(var, newstate)
-            #print "   args", self.getstates(exit.target.inputargs)
             self.scheduled[exit.target] = graph
 
     def flow_operation(self, op):
-        #print "handling", op
         args = self.getstates(op.args)
-        #print "args:", args
         opimpl = getattr(self, 'op_'+op.opname, None)
         if opimpl is not None:
             res = opimpl(op, *args)
@@ -194,18 +170,21 @@
         if isonheap(op.result) or filter(None, args):
             for arg in args:
                 if arg is not None:
-                    changed = arg.setchanges()
-                    self.handle_changed(changed)
-                    changed = arg.setescapes()
-                    self.handle_changed(changed)
-            #raise NotImplementedError("can't handle %s" % (op.opname, ))
-            #print "assuming that '%s' is irrelevant" % op
+                    self.escapes(arg)
         
     def complete(self):
         while self.scheduled:
             block, graph = self.scheduled.popitem()
             self.flow_block(block, graph)
 
+    def escapes(self, arg):
+        changed = arg.setescapes()
+        self.handle_changed(changed)
+
+    def returns(self, arg):
+        changed = arg.setreturns()
+        self.handle_changed(changed)
+
     def handle_changed(self, changed):
         for crep in changed:
             if crep not in self.dependencies:
@@ -222,13 +201,10 @@
     def register_state_dependency(self, state1, state2):
         "state1 depends on state2: if state2 does escape/change, so does state1"
         # change state1 according to how state2 is now
-        #print "registering dependency of %s on %s" % (state1, state2)
         if state2.does_escape():
-            changed = state1.setescapes()  # mark all crep's as escaping
-            self.handle_changed(changed)
-        if state2.does_change():
-            changed = state1.setchanges()  # mark all crep's as changing
-            self.handle_changed(changed)
+            self.escapes(state1)
+        if state2.does_return():
+            self.returns(state1)
         # register a dependency of the current block on state2:
         # that means that if state2 changes the current block will be reflown
         # triggering this function again and thus updating state1
@@ -242,14 +218,14 @@
         flags = op.args[1].value
         if flags != {'flavor': 'gc'}:
             return NotImplemented
-        return VarState(self.get_creationpoint(op.result, "malloc"))
+        return VarState(self.get_creationpoint(op.result, "malloc", op))
 
     def op_malloc_varsize(self, op, typestate, flagsstate, lengthstate):
         assert flagsstate is None
         flags = op.args[1].value
         if flags != {'flavor': 'gc'}:
             return NotImplemented
-        return VarState(self.get_creationpoint(op.result, "malloc_varsize"))
+        return VarState(self.get_creationpoint(op.result, "malloc_varsize", op))
 
     def op_keepalive(self, op, state):
         return None
@@ -258,49 +234,26 @@
         return state
     
     def op_setfield(self, op, objstate, fieldname, valuestate):
-        changed = objstate.setchanges()
-        self.handle_changed(changed)
         if valuestate is not None:
             # be pessimistic for now:
-            # everything that gets stored into a structure escapes and changes
-            self.handle_changed(changed)
-            changed = valuestate.setchanges()
-            self.handle_changed(changed)
-            changed = valuestate.setescapes()
-            self.handle_changed(changed)
+            # everything that gets stored into a structure escapes
+            self.escapes(valuestate)
         return None
 
     def op_setarrayitem(self, op, objstate, indexstate, valuestate):
-        changed = objstate.setchanges()
-        self.handle_changed(changed)
         if valuestate is not None:
-            # everything that gets stored into a structure escapes and changes
-            self.handle_changed(changed)
-            changed = valuestate.setchanges()
-            self.handle_changed(changed)
-            changed = valuestate.setescapes()
-            self.handle_changed(changed)
+            # everything that gets stored into a structure escapes
+            self.escapes(valuestate)
         return None
 
     def op_getarrayitem(self, op, objstate, indexstate):
         if isonheap(op.result):
-            return VarState(self.get_creationpoint(op.result, "getarrayitem"))
+            return VarState(self.get_creationpoint(op.result, "getarrayitem", op))
     
     def op_getfield(self, op, objstate, fieldname):
         if isonheap(op.result):
             # assume that getfield creates a new value
-            return VarState(self.get_creationpoint(op.result, "getfield"))
-
-    def op_getsubstruct(self, op, objstate, fieldname):
-        # since this is really an embedded struct, it has the same
-        # state, the same creationpoints, etc.
-        return objstate
-
-    def op_getarraysubstruct(self, op, arraystate, indexstate):
-        # since this is really a struct embedded somewhere in the array it has
-        # the same state, creationpoints, etc. in most cases the resulting
-        # pointer should not be used much anyway
-        return arraystate
+            return VarState(self.get_creationpoint(op.result, "getfield", op))
 
     def op_getarraysize(self, op, arraystate):
         pass
@@ -311,9 +264,8 @@
             for arg in args:
                 if arg is None:
                     continue
-                # an external function can change every parameter:
-                changed = arg.setchanges()
-                self.handle_changed(changed)
+                # an external function can escape every parameter:
+                self.escapes(arg)
             funcargs = [None] * len(args)
         else:
             result, funcargs = self.schedule_function(graph)
@@ -326,7 +278,7 @@
                 self.register_state_dependency(localarg, funcarg)
         if isonheap(op.result):
             # assume that a call creates a new value
-            return VarState(self.get_creationpoint(op.result, "direct_call"))
+            return VarState(self.get_creationpoint(op.result, "direct_call", op))
 
     def op_indirect_call(self, op, function, *args):
         graphs = op.args[-1].value
@@ -335,10 +287,7 @@
             for localarg in args:
                 if localarg is None:
                     continue
-                changed = localarg.setescapes()
-                self.handle_changed(changed)
-                changed = localarg.setchanges()
-                self.handle_changed(changed)
+                self.escapes(localarg)
         else:
             for graph in graphs:
                 result, funcargs = self.schedule_function(graph)
@@ -350,7 +299,7 @@
                     self.register_state_dependency(localarg, funcarg)
         if isonheap(op.result):
             # assume that a call creates a new value
-            return VarState(self.get_creationpoint(op.result, "indirect_call"))
+            return VarState(self.get_creationpoint(op.result, "indirect_call", op))
 
     def op_ptr_iszero(self, op, ptrstate):
         return None
@@ -377,35 +326,38 @@
             return False
     return True
 
-def malloc_to_stack(t):
-    adi = AbstractDataFlowInterpreter(t)
-    for graph in t.graphs:
-        if graph.startblock not in adi.flown_blocks:
-            adi.schedule_function(graph)
-            adi.complete()
-    for graph in t.graphs:
-        loop_blocks = support.find_loop_blocks(graph)
-        for block, op in graph.iterblockops():
-            if op.opname != 'malloc':
-                continue
-            STRUCT = op.args[0].value
-            # 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:
-                    continue
-            except (ValueError, AttributeError), e:
-                pass
-            varstate = adi.getstate(op.result)
-            assert len(varstate.creation_points) == 1
-            crep = varstate.creation_points.keys()[0]
-            if not crep.escapes:
-                if block not in loop_blocks:
-                    print "moving object from heap to stack %s in %s" % (op, graph.name)
-                    flags = op.args[1].value
-                    assert flags == {'flavor': 'gc'}
-                    op.args[1] = Constant({'flavor': 'stack'}, lltype.Void)
-                else:
-                    print "%s in %s is a non-escaping malloc in a loop" % (op, graph.name)
+
+def is_malloc_like(adi, graph, seen):
+    if graph in seen:
+        return seen[graph]
+    return_state = adi.getstate(graph.getreturnvar())
+    if return_state is None or len(return_state.creation_points) != 1:
+        seen[graph] = False
+        return False
+    crep, = return_state.creation_points
+    if crep.escapes:
+        seen[graph] = False
+        return False
+    if crep.creation_method in ["malloc", "malloc_varsize"]:
+        assert crep.returns
+        seen[graph] = True
+        return True
+    if crep.creation_method == "direct_call":
+        subgraph = get_graph(crep.op.args[0], adi.translation_context)
+        if subgraph is None:
+            seen[graph] = False
+            return False
+        res = is_malloc_like(adi, subgraph, seen)
+        seen[graph] = res
+        return res
+    seen[graph] = False
+    return False
+
+
+def malloc_like_graphs(adi):
+    seen = {}
+    return [graph for graph in adi.seen_graphs()
+        if is_malloc_like(adi, graph, seen)]
+
 
 

Modified: pypy/trunk/pypy/translator/backendopt/test/test_escape.py
==============================================================================
--- pypy/trunk/pypy/translator/backendopt/test/test_escape.py	(original)
+++ pypy/trunk/pypy/translator/backendopt/test/test_escape.py	Mon Oct 26 11:25:33 2009
@@ -1,41 +1,22 @@
-from pypy.translator.translator import TranslationContext, graphof
-from pypy.translator.backendopt.escape import AbstractDataFlowInterpreter, malloc_to_stack
-from pypy.translator.backendopt.support import find_backedges, find_loop_blocks
-from pypy.rpython.llinterp import LLInterpreter
+from pypy.translator.interactive import Translation
+from pypy.translator.translator import graphof
+from pypy.translator.backendopt.escape import AbstractDataFlowInterpreter
+from pypy.translator.backendopt.escape import malloc_like_graphs
 from pypy.rlib.objectmodel import instantiate
 from pypy import conftest
 
 import py
 
 def build_adi(function, types):
-    t = TranslationContext()
-    t.buildannotator().build_types(function, types)
-    t.buildrtyper().specialize()
+    t = Translation(function)
+    t.rtype(types)
     if conftest.option.view:
         t.view()
-    adi = AbstractDataFlowInterpreter(t)
-    graph = graphof(t, function)
+    adi = AbstractDataFlowInterpreter(t.context)
+    graph = graphof(t.context, function)
     adi.schedule_function(graph)
     adi.complete()
-    return t, adi, graph
-
-def check_malloc_removal(function, types, args, expected_result, must_remove=True):
-    t = TranslationContext()
-    t.buildannotator().build_types(function, types)
-    t.buildrtyper().specialize()
-    interp = LLInterpreter(t.rtyper)
-    graph = graphof(t, function)
-    res = interp.eval_graph(graph, args)
-    assert res == expected_result
-    malloc_to_stack(t)
-    if must_remove:
-        for block in graph.iterblocks():
-            for op in block.operations:
-                if op.opname == "malloc":
-                    assert op.args[1].value['flavor'] == 'stack'
-    res = interp.eval_graph(graph, args)
-    assert res == expected_result
-    return t
+    return t.context, adi, graph
 
 def test_simple():
     class A(object):
@@ -47,9 +28,7 @@
     t, adi, graph = build_adi(f, [])
     avar = graph.startblock.operations[0].result
     state = adi.getstate(avar)
-    assert len(state.creation_points) == 1
-    crep = state.creation_points.keys()[0]
-    assert crep.changes
+    crep, = state.creation_points
     assert not crep.escapes
 
 def test_branch():
@@ -66,9 +45,7 @@
     t, adi, graph = build_adi(fn2, [int, int])
     tvar = graph.startblock.operations[0].result
     state = adi.getstate(tvar)
-    assert len(state.creation_points) == 1
-    crep = state.creation_points.keys()[0]
-    assert crep.changes
+    crep, = state.creation_points
     assert not crep.escapes
 
 def test_loop():
@@ -85,9 +62,7 @@
     t, adi, graph = build_adi(f, [])
     avar = graph.startblock.operations[0].result
     state = adi.getstate(avar)
-    assert len(state.creation_points) == 1
-    crep = state.creation_points.keys()[0]
-    assert crep.changes
+    crep, = state.creation_points
     assert not crep.escapes
     avarinloop = graph.startblock.exits[0].target.inputargs[1]
     state1 = adi.getstate(avarinloop)
@@ -106,8 +81,7 @@
     avar = graph.startblock.operations[0].result
     state = adi.getstate(avar)
     assert len(state.creation_points) == 1
-    crep = state.creation_points.keys()[0]
-    assert crep.changes
+    crep, = state.creation_points
     assert crep.escapes
 
 def test_classattrs():
@@ -121,9 +95,7 @@
     t, adi, graph = build_adi(fn5, [])
     bvar = graph.startblock.operations[0].result
     state = adi.getstate(bvar)
-    assert len(state.creation_points) == 1
-    crep = state.creation_points.keys()[0]
-    assert crep.changes
+    crep, = state.creation_points
     assert not crep.escapes
 
 def test_aliasing():
@@ -144,8 +116,7 @@
     avar = graph.startblock.exits[0].target.inputargs[1]
     state = adi.getstate(avar)
     assert len(state.creation_points) == 2
-    for crep in state.creation_points.keys():
-        assert crep.changes
+    for crep in state.creation_points:
         assert not crep.escapes
 
 def test_call():
@@ -161,15 +132,11 @@
     g_graph = graphof(t, g)
     bvar = g_graph.startblock.inputargs[0]
     bstate = adi.getstate(bvar)
-    assert len(bstate.creation_points) == 1
-    bcrep = bstate.creation_points.keys()[0]
-    assert not bcrep.changes
+    bcrep, = bstate.creation_points
     assert not bcrep.escapes
     avar = graph.startblock.operations[0].result
     astate = adi.getstate(avar)
-    assert len(astate.creation_points) == 1
-    acrep = astate.creation_points.keys()[0]
-    assert acrep.changes
+    acrep, = astate.creation_points
     assert not acrep.escapes
 
 def test_dependencies():
@@ -197,10 +164,6 @@
     reallygraph = resizegraph.startblock.exits[0].target.operations[0].args[0].value._obj.graph
     reallyarg0 = reallygraph.startblock.inputargs[0]
     reallystate = adi.getstate(reallyarg0)
-    assert reallystate.does_change()
-    assert resizestate.does_change()
-    assert appendstate.does_change()
-    assert astate.does_change()
 
 def test_substruct():
     class A(object):
@@ -221,14 +184,10 @@
     b0var = graph.startblock.operations[3].result 
     a0state = adi.getstate(a0var)
     b0state = adi.getstate(b0var)
-    assert len(a0state.creation_points) == 1
-    a0crep = a0state.creation_points.keys()[0]
+    a0crep, = a0state.creation_points
     assert not a0crep.escapes
-    assert a0crep.changes
-    assert len(b0state.creation_points) == 1
-    b0crep = b0state.creation_points.keys()[0]
+    b0crep, = b0state.creation_points
     assert b0crep.escapes
-    assert b0crep.changes
 
 def test_multiple_calls():
     class A(object):
@@ -251,14 +210,11 @@
     a1state = adi.getstate(a1var)
     a2state = adi.getstate(a2var)
     a3state = adi.getstate(a3var)
-    assert len(a1state.creation_points) == 1
-    assert len(a2state.creation_points) == 1
-    assert len(a3state.creation_points) == 1
-    a1crep = a1state.creation_points.keys()[0]
-    a2crep = a2state.creation_points.keys()[0]
-    a3crep = a3state.creation_points.keys()[0]
-    assert a1crep.changes and a2crep.changes and a3crep.changes
-    assert not a1crep.escapes and a2crep.escapes and a3crep.escapes
+    a1crep, = a1state.creation_points
+    a2crep, = a2state.creation_points
+    a3crep, = a3state.creation_points
+    assert not a1crep.escapes and not a2crep.escapes and not a3crep.escapes
+    assert not a1crep.returns and a2crep.returns and a3crep.returns
 
 def test_indirect_call():
     class A(object):
@@ -293,12 +249,9 @@
     a2var = graph.startblock.operations[3].result
     a1state = adi.getstate(a1var)
     a2state = adi.getstate(a2var)
-    assert len(a1state.creation_points) == 1
-    assert len(a2state.creation_points) == 1
-    a1crep = a1state.creation_points.keys()[0]
-    a2crep = a2state.creation_points.keys()[0]
-    assert a1crep.changes and a2crep.changes
-    assert not a1crep.escapes and a2crep.escapes
+    a1crep, = a1state.creation_points
+    a2crep, = a2state.creation_points
+    assert not a1crep.escapes and a2crep.returns
 
 def test_indirect_call_unknown_graphs():
     class A:
@@ -324,9 +277,7 @@
     t, adi, graph = build_adi(f, [])
     avar = graph.startblock.operations[0].result
     state = adi.getstate(avar)
-    assert len(state.creation_points) == 1
-    crep = state.creation_points.keys()[0]
-    assert crep.changes
+    crep, = state.creation_points
     assert crep.escapes
 
 def test_flow_blocksonce():
@@ -367,7 +318,6 @@
     t, adi, graph = build_adi(createdict, [int, int])
     dvar = graph.startblock.operations[0].result
     dstate = adi.getstate(dvar)
-    assert dstate.does_change()
     assert not dstate.does_escape()
 
 def test_raise_escapes():
@@ -378,7 +328,18 @@
     avar = graph.startblock.operations[0].result
     state = adi.getstate(avar)
     assert state.does_escape()
-    assert state.does_change()
+
+def test_return():
+    class A(object):
+        pass
+    def f():
+        a = A()
+        return a
+    t, adi, graph = build_adi(f, [])
+    avar = graph.startblock.operations[0].result
+    state = adi.getstate(avar)
+    assert not state.does_escape()
+    assert state.does_return()
 
 
 def test_big():
@@ -387,95 +348,23 @@
     # does not crash
     t, adi, graph = build_adi(entrypoint, [int])
 
-def test_extfunc_onheaparg():
-    py.test.skip("not a valid test anymore")
-    import os
-    def f(i):
-        s = str(i)
-        os.write(2, s)
-        return len(s)
-    t, adi, graph = build_adi(f, [int])
-    svar = graph.startblock.operations[0].result
-    state = adi.getstate(svar)
-    assert not state.does_escape()
-    assert state.does_change()
-    
-def test_extfunc_resultonheap():
-    py.test.skip("not a valid test anymore")
-    import os
-    def f(i):
-        s = str(i)
-        return len(s)
-    t, adi, graph = build_adi(f, [float])
-    svar = graph.startblock.operations[0].result
-    state = adi.getstate(svar)
-    assert not state.does_escape()
-
-
-
-#__________________________________________________________
-# malloc removal tests
-
-def test_remove_simple():
-    class A(object):
-        pass
-    def f():
-        a = A()
-        a.x = 1
-        return a.x
-    check_malloc_removal(f, [], [], 1)
-
-def test_remove_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
-    t = check_malloc_removal(fn6, [int], [2], 12)
-
-def test_remove_call():
+def test_find_malloc_like_graphs():
     class A(object):
         pass
-    def g(b):
-        return b.i + 2
-    def f():
+    def f(x):
         a = A()
-        a.i = 2
-        return g(a)
-    t = check_malloc_removal(f, [], [], 4)
+        a.x = x
+        return a
 
-def test_dont_alloca_in_loops():
-    class A(object):
-        pass
-    def f(x):
-        result = 0
-        for i in range(x):
-            a = A()
-            a.i = i
-            result += a.i
-        return result
-    t = check_malloc_removal(f, [int], [3], 3, must_remove=False)
-    graph = graphof(t, f)
-    assert graph.startblock.exits[0].target.exits[0].target.operations[0].opname == "malloc"
+    def g(a):
+        return a
 
-def test_dont_remove_del_objects():
-    class A(object):
-        def __del__(self):
-            pass
-    def f():
-        a = A()
-        a.i = 1
-        return a.i        
-    t = check_malloc_removal(f, [], [], 1, must_remove=False)
-    graph = graphof(t, f)
-    assert graph.startblock.operations[0].opname == "malloc"
-   
+    def h(x):
+        return f(x + 1)
+    def main(x):
+        return f(x).x + g(h(x)).x
+
+    t, adi, graph = build_adi(main, [int])
+    graphs = malloc_like_graphs(adi)
+    assert [g.name for g in graphs] == ["f", "h"]
 



More information about the Pypy-commit mailing list