[pypy-svn] r25610 - in pypy/dist/pypy: objspace/flow translator/backendopt translator/backendopt/test

cfbolz at codespeak.net cfbolz at codespeak.net
Sun Apr 9 12:08:20 CEST 2006


Author: cfbolz
Date: Sun Apr  9 12:08:15 2006
New Revision: 25610

Added:
   pypy/dist/pypy/translator/backendopt/mallocprediction.py
   pypy/dist/pypy/translator/backendopt/test/test_mallocprediction.py
Modified:
   pypy/dist/pypy/objspace/flow/model.py
   pypy/dist/pypy/translator/backendopt/escape.py
   pypy/dist/pypy/translator/backendopt/inline.py
Log:
try to find out calls that are interesting to inline because doing that will
probably enable malloc removal.


Modified: pypy/dist/pypy/objspace/flow/model.py
==============================================================================
--- pypy/dist/pypy/objspace/flow/model.py	(original)
+++ pypy/dist/pypy/objspace/flow/model.py	Sun Apr  9 12:08:15 2006
@@ -98,6 +98,11 @@
                 seen[block] = True
                 stack += block.exits[::-1]
 
+    def iterblockops(self):
+        for block in self.iterblocks():
+            for op in block.operations:
+                yield block, op
+
     def show(self):
         from pypy.translator.tool.graphpage import SingleGraphPage
         SingleGraphPage(self).display()

Modified: pypy/dist/pypy/translator/backendopt/escape.py
==============================================================================
--- pypy/dist/pypy/translator/backendopt/escape.py	(original)
+++ pypy/dist/pypy/translator/backendopt/escape.py	Sun Apr  9 12:08:15 2006
@@ -369,25 +369,25 @@
             adi.complete()
     for graph in t.graphs:
         loop_blocks = support.find_loop_blocks(graph)
-        for block in graph.iterblocks():
-            for op in block.operations:
-                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)
-                            op.opname = 'flavored_malloc'
-                            op.args.insert(0, inputconst(lltype.Void, 'stack'))
-                        else:
-                            print "%s in %s is a non-escaping malloc in a loop" % (op, graph.name)
+        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)
+                        op.opname = 'flavored_malloc'
+                        op.args.insert(0, inputconst(lltype.Void, 'stack'))
+                    else:
+                        print "%s in %s is a non-escaping malloc in a loop" % (op, graph.name)
+
 

Modified: pypy/dist/pypy/translator/backendopt/inline.py
==============================================================================
--- pypy/dist/pypy/translator/backendopt/inline.py	(original)
+++ pypy/dist/pypy/translator/backendopt/inline.py	Sun Apr  9 12:08:15 2006
@@ -520,12 +520,14 @@
     return result
 
 
-def auto_inlining(translator, threshold=1):
+def auto_inlining(translator, threshold=1, callgraph=None):
     from heapq import heappush, heappop, heapreplace
     threshold *= BASE_INLINE_THRESHOLD
     callers = {}     # {graph: {graphs-that-call-it}}
     callees = {}     # {graph: {graphs-that-it-calls}}
-    for graph1, graph2 in static_callers(translator, ignore_primitives=True):
+    if callgraph is None:
+        callgraph = static_callers(translator, ignore_primitives=True)
+    for graph1, graph2 in callgraph:
         callers.setdefault(graph2, {})[graph1] = True
         callees.setdefault(graph1, {})[graph2] = True
     heap = [(0.0, -len(callers[graph]), graph) for graph in callers]

Added: pypy/dist/pypy/translator/backendopt/mallocprediction.py
==============================================================================
--- (empty file)
+++ pypy/dist/pypy/translator/backendopt/mallocprediction.py	Sun Apr  9 12:08:15 2006
@@ -0,0 +1,116 @@
+from pypy.translator.backendopt.escape import AbstractDataFlowInterpreter
+from pypy.translator.backendopt.malloc import remove_simple_mallocs
+from pypy.translator.backendopt.inline import auto_inlining
+from pypy.rpython.lltypesystem import lltype
+from pypy.translator.simplify import get_graph
+
+def find_malloc_creps(graph, adi, translator):
+    # mapping from malloc creation point to graphs that it flows into
+    malloc_creps = {}
+    # find all mallocs that don't escape
+    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:
+                malloc_creps[crep] = {}
+    return malloc_creps
+
+def find_calls_where_creps_go(interesting_creps, graph, adi,
+                              translator, seen):
+    print "find_calls_where_creps_go", interesting_creps, graph.name
+    print seen
+    # drop creps that are merged with another creation point
+    for block in graph.iterblocks():
+        for var in block.getvariables():
+            varstate = adi.getstate(var)
+            if varstate is None:
+                continue
+            for crep in varstate.creation_points:
+                if crep in interesting_creps:
+                    if len(varstate.creation_points) != 1:
+                        del interesting_creps[crep]
+                        break
+    
+    # drop creps that are passed into an indirect_call
+    for block, op in graph.iterblockops():
+        if not interesting_creps:
+            return
+        if op.opname == "indirect_call":
+            for var in op.args[:-1]:
+                varstate = adi.getstate(var)
+                for crep in varstate.creation_points:
+                    if crep in interesting_creps:
+                        del interesting_creps[crep]
+        elif op.opname == "direct_call":
+            print op, interesting_creps
+            called_graph = get_graph(op.args[0], translator)
+            if called_graph is None:
+                del interesting_creps[crep]
+                print "graph not found"
+                continue
+            interesting = {}
+            for i, var in enumerate(op.args[1:]):
+                print i, var,
+                varstate = adi.getstate(var)
+                if varstate is None:
+                    print "no varstate"
+                    continue
+                if len(varstate.creation_points) == 1:
+                    crep = varstate.creation_points.keys()[0]
+                    if crep not in interesting_creps:
+                        print "not interesting"
+                        continue
+                    if (called_graph, i) in seen:
+                        seen[(called_graph, i)][graph] = True
+                        print "seen already"
+                    else:
+                        print "taking", crep
+                        seen[(called_graph, i)] = {graph: True}
+                        arg = called_graph.startblock.inputargs[i]
+                        argstate = adi.getstate(arg)
+                        argcrep = [c for c in argstate.creation_points
+                                    if c.creation_method == "arg"][0]
+                        interesting[argcrep] = True
+            print interesting
+            if interesting:
+                find_calls_where_creps_go(interesting, called_graph,
+                                          adi, translator, seen)
+    return interesting_creps
+
+def find_malloc_removal_candidates(t):
+    adi = AbstractDataFlowInterpreter(t)
+    for graph in t.graphs:
+        if graph.startblock not in adi.flown_blocks:
+            adi.schedule_function(graph)
+            adi.complete()
+    caller_candidates = {}
+    seen = {}
+    for graph in t.graphs:
+        creps = find_malloc_creps(graph, adi, t)
+        print "malloc creps", creps
+        if creps:
+            find_calls_where_creps_go(creps, graph, adi, t, seen)
+            if creps:
+                caller_candidates[graph] = True
+    callgraph = []
+    for (called_graph, i), callers in seen.iteritems():
+        for caller in callers:
+            callgraph.append((caller, called_graph))
+    return callgraph, caller_candidates
+
+def inline_and_remove(t, threshold=1):
+    callgraph, caller_candidates = find_malloc_removal_candidates(t)
+    auto_inlining(t, threshold, callgraph)
+    for graph in caller_candidates:
+        remove_simple_mallocs(graph)

Added: pypy/dist/pypy/translator/backendopt/test/test_mallocprediction.py
==============================================================================
--- (empty file)
+++ pypy/dist/pypy/translator/backendopt/test/test_mallocprediction.py	Sun Apr  9 12:08:15 2006
@@ -0,0 +1,86 @@
+import py
+from pypy.translator.translator import TranslationContext
+from pypy.translator.backendopt.inline import inline_function
+from pypy.translator.backendopt.all import backend_optimizations
+from pypy.translator.translator import TranslationContext, graphof
+from pypy.rpython.llinterp import LLInterpreter
+from pypy.objspace.flow.model import checkgraph, flatten, Block
+from pypy.conftest import option
+
+from pypy.translator.backendopt.mallocprediction import *
+
+def compile(fn, signature):
+    t = TranslationContext()
+    t.buildannotator().build_types(fn, signature)
+    t.buildrtyper().specialize()
+    graph = graphof(t, fn)
+    if option.view:
+        t.view()
+    return t, graph
+    
+
+def check_inlining(t, graph, args, result):
+    callgraph, caller_candidates = find_malloc_removal_candidates(t)
+    nice_callgraph = {}
+    for caller, callee in callgraph:
+        nice_callgraph.setdefault(caller, {})[callee] = True
+    inline_and_remove(t)
+    if option.view:
+        t.view()
+    interp = LLInterpreter(t.rtyper)
+    res = interp.eval_graph(graph, args)
+    assert res == result
+    return nice_callgraph, caller_candidates
+
+def test_fn():
+    class A:
+        pass
+    class B(A):
+        pass
+    def g(a, b, i):
+        a.b = b
+        b.i = i
+        return a.b.i
+    def h(x):
+        return x + 42
+    def fn(i):
+        a = A()
+        b = B()
+        x = h(i)
+        return g(a, b, x)
+    t, graph = compile(fn, [int])
+    callgraph, caller_candidates = check_inlining(t, graph, [0], 42)
+    assert caller_candidates == {graph: True}
+    assert len(callgraph) == 1
+    ggraph = graphof(t, g)
+    assert callgraph[graph] == {ggraph: True}
+
+def test_multiple_calls():
+    class A:
+        pass
+    class B(A):
+        pass
+    def g2(b, i): 
+        b.i = h(i)
+    def g1(a, b, i):
+        a.b = b
+        g2(b, h(i))
+        return a.b.i
+    def h(x):
+        return x + 42
+    def fn(i):
+        a = A()
+        b = B()
+        x = h(i)
+        return g1(a, b, x)
+    t, graph = compile(fn, [int])
+    callgraph, caller_candidates = check_inlining(t, graph, [0], 3 * 42)
+    print callgraph
+    assert caller_candidates == {graph: True}
+    assert len(callgraph) == 1
+    g1graph = graphof(t, g1)
+    g2graph = graphof(t, g2)
+    assert callgraph[graph] == {g1graph: True}
+    callgraph, caller_candidates = check_inlining(t, graph, [0], 3 * 42)
+    assert callgraph[graph] == {g2graph: True}
+



More information about the Pypy-commit mailing list