[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