[pypy-svn] r48267 - in pypy/dist/pypy/translator/backendopt: . test
cfbolz at codespeak.net
cfbolz at codespeak.net
Sat Nov 3 15:07:23 CET 2007
Author: cfbolz
Date: Sat Nov 3 15:07:22 2007
New Revision: 48267
Added:
pypy/dist/pypy/translator/backendopt/coalloc.py (contents, props changed)
pypy/dist/pypy/translator/backendopt/test/test_coalloc.py (contents, props changed)
Modified:
pypy/dist/pypy/translator/backendopt/escape.py
Log:
a so far very simplistic analysis to find potential coallocation sites
Added: pypy/dist/pypy/translator/backendopt/coalloc.py
==============================================================================
--- (empty file)
+++ pypy/dist/pypy/translator/backendopt/coalloc.py Sat Nov 3 15:07:22 2007
@@ -0,0 +1,307 @@
+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
+from pypy.rpython.rmodel import inputconst
+from pypy.translator.backendopt import support
+from pypy.tool.uid import uid
+
+class CreationPoint(object):
+ def __init__(self, creation_method, TYPE):
+ self.creation_method = creation_method
+ self.TYPE = TYPE
+
+ def __repr__(self):
+ return ("CreationPoint(<0x%x>, %r)" %
+ (uid(self), self.TYPE))
+
+class VarState(object):
+ def __init__(self, crep=None):
+ self.creation_points = {}
+ if crep is not None:
+ self.creation_points[crep] = True
+ self.returned = False
+
+ def contains(self, other):
+ for crep in other.creation_points:
+ if crep not in self.creation_points:
+ return False
+ return True
+
+ def merge(self, other):
+ creation_points = setunion(self.creation_points, other.creation_points)
+ newstate = VarState()
+ newstate.creation_points = creation_points
+ return newstate
+
+ def __repr__(self):
+ crepsrepr = (", ".join([repr(crep) for crep in self.creation_points]), )
+ return "VarState({%s})" % crepsrepr
+
+class GraphState(object):
+ def __init__(self, graph):
+ self.graph = graph
+
+
+class AbstractDataFlowInterpreter(object):
+ def __init__(self, translation_context):
+ self.translation_context = translation_context
+ self.scheduled = {} # block: graph containing it
+ self.varstates = {} # var-or-const: state
+ self.creationpoints = {} # var: creationpoint
+ self.constant_cps = {} # const: creationpoint
+ self.dependencies = {} # creationpoint: {block: graph containing it}
+ self.functionargs = {} # graph: list of state of args
+ self.flown_blocks = {} # block: True
+
+ def getstate(self, var_or_const):
+ if not isonheap(var_or_const):
+ return None
+ if var_or_const in self.varstates:
+ return self.varstates[var_or_const]
+ if isinstance(var_or_const, Variable):
+ varstate = VarState()
+ else:
+ if var_or_const not in self.constant_cps:
+ crep = CreationPoint("constant", var_or_const.concretetype)
+ self.constant_cps[var_or_const] = crep
+ else:
+ crep = self.constant_cps[var_or_const]
+ varstate = VarState(crep)
+ self.varstates[var_or_const] = varstate
+ return varstate
+
+ def getstates(self, varorconstlist):
+ return [self.getstate(var) for var in varorconstlist]
+
+ def setstate(self, var, state):
+ self.varstates[var] = state
+
+ def get_creationpoint(self, var, method="?"):
+ if var in self.creationpoints:
+ return self.creationpoints[var]
+ crep = CreationPoint(method, var.concretetype)
+ 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]
+ else:
+ args = []
+ for var in startblock.inputargs:
+ if not isonheap(var):
+ varstate = None
+ else:
+ crep = self.get_creationpoint(var, "arg")
+ varstate = VarState(crep)
+ self.setstate(var, varstate)
+ args.append(varstate)
+ self.scheduled[startblock] = graph
+ self.functionargs[graph] = args
+ resultstate = self.getstate(graph.returnblock.inputargs[0])
+ 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]):
+ self.getstate(block.inputargs[0]).returned = True
+ return
+ if block is graph.exceptblock:
+ 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:
+ 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)
+ if res is not NotImplemented:
+ self.setstate(op.result, res)
+ return
+
+ if isonheap(op.result) or filter(None, args):
+ raise NotImplementedError("can't handle %s" % (op.opname, ))
+ #print "assuming that '%s' is irrelevant" % op
+
+ def complete(self):
+ while self.scheduled:
+ block, graph = self.scheduled.popitem()
+ self.flow_block(block, graph)
+
+ def handle_changed(self, changed):
+ for crep in changed:
+ if crep not in self.dependencies:
+ continue
+ self.scheduled.update(self.dependencies[crep])
+
+ def register_block_dependency(self, state, block=None, graph=None):
+ if block is None:
+ block = self.curr_block
+ graph = self.curr_graph
+ for crep in state.creation_points:
+ self.dependencies.setdefault(crep, {})[block] = graph
+
+ 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)
+ # 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
+ self.register_block_dependency(state2)
+
+ # _____________________________________________________________________
+ # operation implementations
+
+ def op_malloc(self, op, typestate, flagsstate):
+ assert flagsstate is None
+ flags = op.args[1].value
+ if flags != {'flavor': 'gc'}:
+ return NotImplemented
+ return VarState(self.get_creationpoint(op.result, "malloc"))
+
+ 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"))
+
+ def op_keepalive(self, op, state):
+ return None
+
+ def op_cast_pointer(self, op, state):
+ return state
+
+ def op_getfield(self, op, objstate, fieldname):
+ # connectivity-wise the field within is identical to the containing
+ # structure
+ return objstate
+ op_getarrayitem = op_getinteriorfield = op_getfield
+
+ def op_getarraysize(self, op, arraystate):
+ pass
+
+ def op_setfield(self, op, objstate, fieldname, valuestate):
+ pass
+ op_setarrayitem = op_setinteriorfield = op_setfield
+
+ def op_direct_call(self, op, function, *args):
+# graph = get_graph(op.args[0], self.translation_context)
+# if graph is None:
+# for arg in args:
+# if arg is None:
+# continue
+# # an external function can change every parameter:
+# changed = arg.setchanges()
+# self.handle_changed(changed)
+# funcargs = [None] * len(args)
+# else:
+# result, funcargs = self.schedule_function(graph)
+# assert len(args) == len(funcargs)
+# for localarg, funcarg in zip(args, funcargs):
+# if localarg is None:
+# assert funcarg is None
+# continue
+# if funcarg is not None:
+# self.register_state_dependency(localarg, funcarg)
+ if isonheap(op.result):
+ # for now assume that a call always creates a new value
+ return VarState(self.get_creationpoint(op.result, "direct_call"))
+
+ def op_indirect_call(self, op, function, *args):
+# graphs = op.args[-1].value
+# args = args[:-1]
+# if graphs is None:
+# for localarg in args:
+# if localarg is None:
+# continue
+# changed = localarg.setescapes()
+# self.handle_changed(changed)
+# changed = localarg.setchanges()
+# self.handle_changed(changed)
+# else:
+# for graph in graphs:
+# result, funcargs = self.schedule_function(graph)
+# assert len(args) == len(funcargs)
+# for localarg, funcarg in zip(args, funcargs):
+# if localarg is None:
+# assert funcarg is None
+# continue
+# self.register_state_dependency(localarg, funcarg)
+ if isonheap(op.result):
+ # for now assume that a call always creates a new value
+ return VarState(self.get_creationpoint(op.result, "indirect_call"))
+
+ def op_ptr_iszero(self, op, ptrstate):
+ return None
+
+ op_cast_ptr_to_int = op_keepalive = op_ptr_nonzero = op_ptr_iszero
+
+ def op_ptr_eq(self, op, ptr1state, ptr2state):
+ return None
+
+ op_ptr_ne = op_ptr_eq
+
+ def op_same_as(self, op, objstate):
+ return objstate
+
+def isonheap(var_or_const):
+ return isinstance(var_or_const.concretetype, lltype.Ptr)
+
+def multicontains(l1, l2):
+ assert len(l1) == len(l2)
+ for a, b in zip(l1, l2):
+ if a is None:
+ assert b is None
+ elif not a.contains(b):
+ return False
+ return True
+
+def malloc_to_coalloc(t):
+ adi = AbstractDataFlowInterpreter(t)
+ for graph in t.graphs:
+ if graph.startblock not in adi.flown_blocks:
+ adi.schedule_function(graph)
+ adi.complete()
+ return adi
Modified: pypy/dist/pypy/translator/backendopt/escape.py
==============================================================================
--- pypy/dist/pypy/translator/backendopt/escape.py (original)
+++ pypy/dist/pypy/translator/backendopt/escape.py Sat Nov 3 15:07:22 2007
@@ -386,25 +386,26 @@
for graph in t.graphs:
loop_blocks = support.find_loop_blocks(graph)
for block, op in graph.iterblockops():
- if op.opname == 'malloc':
- 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)
+ 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)
Added: pypy/dist/pypy/translator/backendopt/test/test_coalloc.py
==============================================================================
--- (empty file)
+++ pypy/dist/pypy/translator/backendopt/test/test_coalloc.py Sat Nov 3 15:07:22 2007
@@ -0,0 +1,114 @@
+from pypy.translator.translator import TranslationContext, graphof
+from pypy.translator.backendopt.coalloc import AbstractDataFlowInterpreter
+from pypy.rpython.llinterp import LLInterpreter
+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()
+ if conftest.option.view:
+ t.view()
+ adi = AbstractDataFlowInterpreter(t)
+ graph = graphof(t, function)
+ adi.schedule_function(graph)
+ adi.complete()
+ return t, adi, graph
+
+def test_simple():
+ class A(object):
+ pass
+ def f():
+ a = A()
+ a.x = 1
+ return a.x
+ 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]
+
+def test_branch():
+ 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
+ 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.creation_method == "malloc"
+
+def test_loop():
+ class A(object):
+ pass
+ def f():
+ a = A()
+ i = 0
+ while i < 3:
+ a.x = i
+ a = A()
+ i += 1
+ return a.x
+ 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.creation_method == "malloc"
+ avarinloop = graph.startblock.exits[0].target.inputargs[1]
+ state1 = adi.getstate(avarinloop)
+ assert crep in state1.creation_points
+ assert len(state1.creation_points) == 2
+
+def test_global():
+ class A(object):
+ pass
+ globala = A()
+ def f():
+ a = A()
+ a.next = None
+ globala.next = a
+ 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.creation_method == "malloc"
+ const = graph.startblock.operations[-1].args[0]
+ state = adi.getstate(const)
+ assert len(state.creation_points) == 1
+ crep = state.creation_points.keys()[0]
+ assert crep.creation_method == "constant"
+
+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
+ t, adi, graph = build_adi(fn6, [int])
+ 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.creation_method == "malloc"
+
More information about the Pypy-commit
mailing list