[pypy-svn] r17505 - in pypy/dist/pypy/translator: . test
arigo at codespeak.net
arigo at codespeak.net
Mon Sep 12 17:43:37 CEST 2005
Author: arigo
Date: Mon Sep 12 17:43:35 2005
New Revision: 17505
Modified:
pypy/dist/pypy/translator/backendoptimization.py
pypy/dist/pypy/translator/simplify.py
pypy/dist/pypy/translator/test/test_backendoptimization.py
Log:
Integrated a simple malloc-removing algorithm. This version is based on
http://codespeak.net/svn/user/arigo/hack/pypy-hack/remove_malloc2.py.
Given a graph, it divides the vars in families, where two vars are in the same
family if a value can go from one to the other across links (or same_as or
cast_pointer operations); the family records all usages of its variables. Then
we just use this information to see which families correspond to
malloc/getfield/setfield only.
The family ("LifeTimes") are probably useful for other kind of analysis as
well, e.g. (samuele) to property detect what exceptions a graph can raise.
Modified: pypy/dist/pypy/translator/backendoptimization.py
==============================================================================
--- pypy/dist/pypy/translator/backendoptimization.py (original)
+++ pypy/dist/pypy/translator/backendoptimization.py Mon Sep 12 17:43:35 2005
@@ -1,6 +1,8 @@
import autopath
from pypy.translator.translator import Translator
-from pypy.translator.simplify import eliminate_empty_blocks, join_blocks, remove_identical_vars
+from pypy.translator.simplify import eliminate_empty_blocks, join_blocks
+from pypy.translator.simplify import remove_identical_vars
+from pypy.translator.simplify import transform_dead_op_vars
from pypy.translator.unsimplify import copyvar, split_block
from pypy.objspace.flow.model import Variable, Constant, Block, Link
from pypy.objspace.flow.model import SpaceOperation, last_exception
@@ -8,7 +10,7 @@
from pypy.annotation import model as annmodel
from pypy.tool.unionfind import UnionFind
from pypy.rpython.lltype import Void, Bool
-from pypy.rpython import rmodel
+from pypy.rpython import rmodel, lltype
def remove_same_as(graph):
"""Remove all 'same_as' operations.
@@ -354,12 +356,231 @@
join_blocks(graph)
remove_identical_vars(graph)
+# ____________________________________________________________
+
+class Blocked(Exception):
+ pass
+
+class LifeTime:
+
+ def __init__(self, (block, var)):
+ assert isinstance(var, Variable)
+ self.variables = {(block, var) : True}
+ self.creationpoints = {} # set of ("type of creation point", ...)
+ self.usepoints = {} # set of ("type of use point", ...)
+
+ def update(self, other):
+ self.variables.update(other.variables)
+ self.creationpoints.update(other.creationpoints)
+ self.usepoints.update(other.usepoints)
+
+
+def compute_lifetimes(graph):
+ """Compute the static data flow of the graph: returns a list of LifeTime
+ instances, each of which corresponds to a set of Variables from the graph.
+ The variables are grouped in the same LifeTime if a value can pass from
+ one to the other by following the links. Each LifeTime also records all
+ places where a Variable in the set is used (read) or build (created).
+ """
+ lifetimes = UnionFind(LifeTime)
+
+ def set_creation_point(block, var, *cp):
+ _, _, info = lifetimes.find((block, var))
+ info.creationpoints[cp] = True
+
+ def set_use_point(block, var, *up):
+ _, _, info = lifetimes.find((block, var))
+ info.usepoints[up] = True
+
+ def union(block1, var1, block2, var2):
+ if isinstance(var1, Variable):
+ lifetimes.union((block1, var1), (block2, var2))
+ elif isinstance(var1, Constant):
+ set_creation_point(block2, var2, "constant", var1)
+ else:
+ raise TypeError(var1)
+
+ for var in graph.startblock.inputargs:
+ set_creation_point(graph.startblock, var, "inputargs")
+ set_use_point(graph.returnblock, graph.returnblock.inputargs[0], "return")
+ set_use_point(graph.exceptblock, graph.exceptblock.inputargs[0], "except")
+ set_use_point(graph.exceptblock, graph.exceptblock.inputargs[1], "except")
+
+ def visit(node):
+ if isinstance(node, Block):
+ for op in node.operations:
+ if op.opname in ("same_as", "cast_pointer"):
+ # special-case these operations to identify their input
+ # and output variables
+ union(node, op.args[0], node, op.result)
+ else:
+ for i in range(len(op.args)):
+ if isinstance(op.args[i], Variable):
+ set_use_point(node, op.args[i], "op", node, op, i)
+ set_creation_point(node, op.result, "op", node, op)
+
+ if isinstance(node, Link):
+ if isinstance(node.last_exception, Variable):
+ set_creation_point(node.prevblock, node.last_exception,
+ "last_exception")
+ if isinstance(node.last_exc_value, Variable):
+ set_creation_point(node.prevblock, node.last_exc_value,
+ "last_exc_value")
+ for i in range(len(node.args)):
+ union(node.prevblock, node.args[i],
+ node.target, node.target.inputargs[i])
+
+ traverse(visit, graph)
+ return lifetimes.infos()
+
+def _try_inline_malloc(info):
+ """Try to inline the mallocs creation and manipulation of the Variables
+ in the given LifeTime."""
+ # the values must be only ever created by a "malloc"
+ lltypes = {}
+ for cp in info.creationpoints:
+ if cp[0] != "op":
+ return False
+ op = cp[2]
+ if op.opname != "malloc":
+ return False
+ lltypes[op.result.concretetype] = True
+
+ # there must be a single largest malloced GcStruct;
+ # all variables can point to it or to initial substructures
+ if len(lltypes) != 1:
+ return False
+ STRUCT = lltypes.keys()[0].TO
+ assert isinstance(STRUCT, lltype.GcStruct)
+
+ # must be only ever accessed via getfield/setfield
+ for up in info.usepoints:
+ if up[0] != "op":
+ return False
+ if (up[2].opname, up[3]) not in [("getfield", 0), ("setfield", 0)]:
+ return False
+
+ # success: replace each variable with a family of variables (one per field)
+ example = STRUCT._container_example()
+ flatnames = []
+ flatconstants = {}
+ def flatten(S, example):
+ start = 0
+ if S._names and isinstance(S._flds[S._names[0]], lltype.GcStruct):
+ flatten(S._flds[S._names[0]], getattr(example, S._names[0]))
+ start = 1
+ for name in S._names[start:]:
+ flatnames.append((S, name))
+ constant = Constant(getattr(example, name))
+ constant.concretetype = lltype.typeOf(constant.value)
+ flatconstants[S, name] = constant
+ flatten(STRUCT, example)
+
+ pending = info.variables.keys()
+ for block, var in pending:
+ newvarsmap = {}
+
+ def var_comes_from_outside():
+ for key in flatnames:
+ newvar = Variable()
+ newvar.concretetype = flatconstants[key].concretetype
+ newvarsmap[key] = newvar
+
+ def var_is_created_here():
+ newvarsmap.update(flatconstants)
+
+ def make_newvars():
+ return [newvarsmap[key] for key in flatnames]
+
+ if var in block.inputargs:
+ var_comes_from_outside()
+ i = block.inputargs.index(var)
+ block.inputargs = (block.inputargs[:i] + make_newvars() +
+ block.inputargs[i+1:])
+
+ assert block.operations != ()
+ newops = []
+ try:
+ for op in block.operations:
+ assert var not in op.args[1:] # should be the first arg only
+ if op.args and var == op.args[0]:
+ if op.opname == "getfield":
+ S = var.concretetype.TO
+ fldname = op.args[1].value
+ newop = SpaceOperation("same_as",
+ [newvarsmap[S, fldname]],
+ op.result)
+ newops.append(newop)
+ elif op.opname == "setfield":
+ S = var.concretetype.TO
+ fldname = op.args[1].value
+ assert (S, fldname) in newvarsmap
+ newvarsmap[S, fldname] = op.args[2]
+ elif op.opname in ("same_as", "cast_pointer"):
+ # temporary pseudo-operation, should be removed below
+ newop = SpaceOperation("_tmp_same_as",
+ make_newvars(),
+ op.result)
+ newops.append(newop)
+ else:
+ raise AssertionError, op.opname
+ elif var == op.result:
+ assert not newvarsmap
+ if op.opname == "malloc":
+ var_is_created_here()
+ elif op.opname in ("same_as", "cast_pointer"):
+ # in a 'v2=same_as(v1)', we must analyse v1 before
+ # we can analyse v2. If we get them in the wrong
+ # order we cancel and reschedule v2.
+ raise Blocked
+ elif op.opname == "_tmp_same_as":
+ # pseudo-operation just introduced by the code
+ # some lines above.
+ for key, v in zip(flatnames, op.args):
+ newvarsmap[key] = v
+ else:
+ raise AssertionError, op.opname
+ else:
+ newops.append(op)
+ except Blocked:
+ pending.append((block, var))
+ continue
+ block.operations[:] = newops
+
+ for link in block.exits:
+ while var in link.args:
+ i = link.args.index(var)
+ link.args = link.args[:i] + make_newvars() + link.args[i+1:]
+
+ return True
+
+def remove_mallocs_once(graph):
+ """Perform one iteration of malloc removal."""
+ lifetimes = compute_lifetimes(graph)
+ progress = False
+ for info in lifetimes:
+ if _try_inline_malloc(info):
+ progress = True
+ return progress
+
+def remove_simple_mallocs(graph):
+ """Iteratively remove (inline) the mallocs that can be simplified away."""
+ done_something = False
+ while remove_mallocs_once(graph):
+ done_something = True
+ return done_something
+
+# ____________________________________________________________
+
def backend_optimizations(graph):
remove_same_as(graph)
eliminate_empty_blocks(graph)
checkgraph(graph)
SSI_to_SSA(graph)
- checkgraph(graph)
+ #checkgraph(graph)
+ if remove_simple_mallocs(graph):
+ transform_dead_op_vars(graph) # typical after malloc removal
+ checkgraph(graph)
# ____________________________________________________________
Modified: pypy/dist/pypy/translator/simplify.py
==============================================================================
--- pypy/dist/pypy/translator/simplify.py (original)
+++ pypy/dist/pypy/translator/simplify.py Mon Sep 12 17:43:35 2005
@@ -341,7 +341,7 @@
pos neg nonzero abs hex oct ord invert add sub mul
truediv floordiv div mod divmod pow lshift rshift and_ or_
xor int float long lt le eq ne gt ge cmp coerce contains
- iter get '''.split():
+ iter get same_as cast_pointer '''.split():
CanRemove[_op] = True
del _op
CanRemoveBuiltins = {
Modified: pypy/dist/pypy/translator/test/test_backendoptimization.py
==============================================================================
--- pypy/dist/pypy/translator/test/test_backendoptimization.py (original)
+++ pypy/dist/pypy/translator/test/test_backendoptimization.py Mon Sep 12 17:43:35 2005
@@ -1,8 +1,9 @@
from pypy.translator.backendoptimization import remove_void, inline_function
+from pypy.translator.backendoptimization import remove_simple_mallocs
from pypy.translator.translator import Translator
from pypy.rpython.lltype import Void
from pypy.rpython.llinterp import LLInterpreter
-from pypy.objspace.flow.model import checkgraph
+from pypy.objspace.flow.model import checkgraph, flatten, Block
from pypy.translator.test.snippet import simple_method, is_perfect_number
from pypy.translator.llvm.log import log
@@ -196,4 +197,54 @@
a.simplify()
t.specialize()
t.view()
-
+
+
+def check_malloc_removed(fn, signature, expected_remaining_mallocs):
+ t = Translator(fn)
+ t.annotate(signature)
+ t.specialize()
+ graph = t.getflowgraph()
+ remove_simple_mallocs(graph)
+ checkgraph(graph)
+ count = 0
+ for node in flatten(graph):
+ if isinstance(node, Block):
+ for op in node.operations:
+ if op.opname == 'malloc':
+ count += 1
+ assert count == expected_remaining_mallocs
+
+def test_remove_mallocs():
+ def fn1(x, y):
+ s, d = x+y, x-y
+ return s*d
+ yield check_malloc_removed, fn1, [int, int], 0
+ #
+ 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
+ yield check_malloc_removed, fn2, [int, int], 0
+ #
+ def fn3(x):
+ a, ((b, c), d, e) = x+1, ((x+2, x+3), x+4, x+5)
+ return a+b+c+d+e
+ yield check_malloc_removed, fn3, [int], 0
+ #
+ class A:
+ pass
+ class B(A):
+ pass
+ def fn4(i):
+ a = A()
+ b = B()
+ a.b = b
+ b.i = i
+ return a.b.i
+ yield check_malloc_removed, fn4, [int], 0
More information about the Pypy-commit
mailing list