[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