[pypy-svn] r25449 - in pypy/dist/pypy/translator/backendopt: . test

cfbolz at codespeak.net cfbolz at codespeak.net
Thu Apr 6 16:51:48 CEST 2006


Author: cfbolz
Date: Thu Apr  6 16:51:44 2006
New Revision: 25449

Modified:
   pypy/dist/pypy/translator/backendopt/propagate.py
   pypy/dist/pypy/translator/backendopt/test/test_propagate.py
Log:
clean up propagate to be a bit less insane:
  * use lltypesystem.lloperation
  * do not use visit

also implement a transform that removes superfluous cast_pointers (which speeds
up refcounting and the transformer gcs).


Modified: pypy/dist/pypy/translator/backendopt/propagate.py
==============================================================================
--- pypy/dist/pypy/translator/backendopt/propagate.py	(original)
+++ pypy/dist/pypy/translator/backendopt/propagate.py	Thu Apr  6 16:51:44 2006
@@ -1,7 +1,7 @@
 from pypy.objspace.flow.model import Block, Variable, Constant, c_last_exception
 from pypy.objspace.flow.model import traverse, mkentrymap, checkgraph
 from pypy.objspace.flow.model import SpaceOperation
-from pypy.rpython.lltypesystem.lltype import Bool
+from pypy.rpython.lltypesystem import lltype, lloperation
 from pypy.rpython.llinterp import LLInterpreter, LLFrame
 from pypy.translator import simplify
 from pypy.translator.simplify import get_graph
@@ -23,22 +23,20 @@
     inlining of functions that return a bool."""
     entrymap = mkentrymap(graph)
     candidates = {}
-    def visit(block):
-        if not isinstance(block, Block):
-            return
+    for block in graph.iterblocks():
         if (isinstance(block.exitswitch, Variable) and
-            block.exitswitch.concretetype is Bool):
+            block.exitswitch.concretetype is lltype.Bool):
             for val, link in enumerate(block.exits):
                 val = bool(val)
-                if block.exitswitch not in link.args:
+                try:
+                    index = link.args.index(block.exitswitch)
+                except ValueError:
                     continue
                 if len(link.target.operations) > 0:
                     continue
-                index = link.args.index(block.exitswitch)
                 var = link.target.inputargs[index]
                 if link.target.exitswitch is var:
                     candidates[block] = val
-    traverse(visit, graph)
     for block, val in candidates.iteritems():
         link = block.exits[val]
         args = []
@@ -52,29 +50,25 @@
         link.args = args
     if candidates:
         print "rewiring links in graph", graph.name
-        checkgraph(graph)
+        simplify.join_blocks(graph)
         return True
     return False
 
 def coalesce_links(graph):
-    candidates = {}
-    def visit(block):
-        if not isinstance(block, Block):
-            return
+    done = False
+    for block in graph.iterblocks():
         if len(block.exits) != 2:
-            return
+            continue
         if block.exitswitch == c_last_exception:
-            return
-        if (block.exits[0].args == block.exits[1].args and
+            continue
+        if not (block.exits[0].args == block.exits[1].args and
             block.exits[0].target is block.exits[1].target):
-            candidates[block] = True
-    traverse(visit, graph)
-    for block in candidates:
+            continue
+        done = True
         block.exitswitch = None
         block.exits = block.exits[:1]
         block.exits[0].exitcase = None
-    if candidates:
-        print "coalescing links in graph", graph.name
+    if done:
         return True
     else:
         return False
@@ -84,7 +78,7 @@
     if all blocks leading to it have the same constant in that position"""
     entrymap = mkentrymap(graph)
     candidates = []
-    changed = [False]
+    changed = False
     for block, ingoing in entrymap.iteritems():
         if block in [graph.returnblock, graph.exceptblock]:
             continue
@@ -108,26 +102,13 @@
             del block.inputargs[i]
             op = SpaceOperation("same_as", [const], var)
             block.operations.insert(0, op)
-            changed[0] = True
-    if changed[0]:
+            changed = True
+    if changed:
         remove_same_as(graph)
         checkgraph(graph)
         return True
     return False
 
-_op = """getarrayitem setarrayitem malloc malloc_varsize flavored_malloc
-         flavored_free getfield setfield getsubstruct getarraysubstruct
-         getarraysize raw_malloc raw_free raw_memcopy raw_load
-         raw_store direct_call indirect_call cast_pointer
-         cast_ptr_to_int""".split()
-from pypy.objspace.flow.operation import FunctionByName
-_op += FunctionByName.keys() #operations with PyObjects are dangerous
-cannot_constant_fold = {}
-for opname in _op:
-    cannot_constant_fold[opname] = True
-del _op
-del FunctionByName
-
 class TooManyOperations(Exception):
     pass
 
@@ -149,14 +130,12 @@
     """do constant folding if the arguments of an operations are constants"""
     lli = LLInterpreter(translator.rtyper)
     llframe = LLFrame(graph, None, lli)
-    changed = [False]
-    def visit(block):
-        if not isinstance(block, Block):
-            return
+    changed = False
+    for block in graph.iterblocks():
         for i, op in enumerate(block.operations):
             if sum([isinstance(arg, Variable) for arg in op.args]):
                 continue
-            if op.opname not in cannot_constant_fold:
+            if lloperation.LL_OPERATIONS[op.opname].canfold:
                 print "folding operation", op, "in graph", graph.name
                 try:
                     llframe.eval_operation(op)
@@ -168,8 +147,9 @@
                     res.concretetype = op.result.concretetype
                     block.operations[i].opname = "same_as"
                     block.operations[i].args = [res]
-                    changed[0] = True
-            elif op.opname == "direct_call":
+                    changed = True
+            # disabling the code for now, since it is not correct
+            elif 0: #op.opname == "direct_call":
                 called_graph = get_graph(op.args[0], translator)
                 if (called_graph is not None and
                     simplify.has_no_side_effects(translator, called_graph) and
@@ -189,10 +169,9 @@
                         res.concretetype = op.result.concretetype
                         block.operations[i].opname = "same_as"
                         block.operations[i].args = [res]
-                        changed[0] = True
+                        changed = True
         block.operations = [op for op in block.operations if op is not None]
-    traverse(visit, graph)
-    if changed[0]:
+    if changed:
         remove_same_as(graph)
         propagate_consts(graph)
         checkgraph(graph)
@@ -208,7 +187,7 @@
             return
         usedvars = {}
         for op in block.operations:
-            if op.opname in cannot_constant_fold:
+            if not lloperation.LL_OPERATIONS[op.opname].canfold:
                 return
             for arg in op.args:
                 if (isinstance(arg, Variable) and arg in block.inputargs):
@@ -270,14 +249,67 @@
     else:
         return False
 
+def remove_duplicate_casts(graph, translator):
+    simplify.join_blocks(graph)
+    num_removed = 0
+    # remove chains of casts
+    for block in graph.iterblocks():
+        comes_from = {}
+        for op in block.operations:
+            if op.opname == "cast_pointer":
+                if op.args[0] in comes_from:
+                    from_var = comes_from[op.args[0]]
+                    comes_from[op.result] = from_var
+                    if from_var.concretetype == op.result.concretetype:
+                        op.opname = "same_as"
+                        op.args = [from_var]
+                        num_removed += 1
+                    else:
+                        op.args = [from_var]
+                else:
+                    comes_from[op.result] = op.args[0]
+    if num_removed:
+        remove_same_as(graph)
+    # remove duplicate casts
+    for block in graph.iterblocks():
+        available = {}
+        for op in block.operations:
+            if op.opname == "cast_pointer":
+                key = (op.args[0], op.result.concretetype)
+                if key in available:
+                    op.opname = "same_as"
+                    op.args = [available[key]]
+                    num_removed += 1
+                else:
+                    available[key] = op.result
+    if num_removed:
+        remove_same_as(graph)
+        # remove casts with unused results
+        for block in graph.iterblocks():
+            used = {}
+            for link in block.exits:
+                for arg in link.args:
+                    used[arg] = True
+            for i, op in list(enumerate(block.operations))[::-1]:
+                if op.opname == "cast_pointer" and op.result not in used:
+                    del block.operations[i]
+                    num_removed += 1
+                else:
+                    for arg in op.args:
+                        used[arg] = True
+        print "removed %s cast_pointers in %s" % (num_removed, graph.name)
+    return num_removed
+
 def propagate_all(translator):
     for graph in translator.graphs:
         def prop():
-            changed = rewire_links(graph)
-            changed = changed or propagate_consts(graph)
-            changed = changed or coalesce_links(graph)
-            changed = changed or do_atmost(100, constant_folding, graph,
-                                           translator)
-            changed = changed or partial_folding(graph, translator)
+            changed = False
+            changed = rewire_links(graph) or changed
+            changed = propagate_consts(graph) or changed
+            changed = coalesce_links(graph) or changed
+            changed = do_atmost(100, constant_folding, graph,
+                                           translator) or changed
+            changed = partial_folding(graph, translator) or changed
+            changed = remove_duplicate_casts(graph, translator) or changed
             return changed
         do_atmost(10, prop)

Modified: pypy/dist/pypy/translator/backendopt/test/test_propagate.py
==============================================================================
--- pypy/dist/pypy/translator/backendopt/test/test_propagate.py	(original)
+++ pypy/dist/pypy/translator/backendopt/test/test_propagate.py	Thu Apr  6 16:51:44 2006
@@ -1,8 +1,10 @@
+import py
 from pypy.translator.translator import TranslationContext, graphof
 from pypy.translator.backendopt.propagate import *
 from pypy.translator.backendopt.all import backend_optimizations
 from pypy.rpython.llinterp import LLInterpreter
-
+from pypy.rpython.memory.test.test_gctransform import getops
+from pypy import conftest
 
 def get_graph(fn, signature, inline_threshold=True):
     t = TranslationContext()
@@ -11,6 +13,8 @@
     backend_optimizations(t, inline_threshold=inline_threshold,
                           ssa_form=False, propagate=False) 
     graph = graphof(t, fn)
+    if conftest.option.view:
+        t.view()
     return graph, t
 
 def check_graph(graph, args, expected_result, t):
@@ -36,9 +40,38 @@
         return ret
     graph, t = get_graph(g, [int])
     propagate_consts(graph)
+    if conftest.option.view:
+        t.view()
     assert len(graph.startblock.exits[0].args) == 4
     check_graph(graph, [100], g(100), t)
-    
+
+def test_rewire_links():
+    def f(x):
+        return x != 1 and x != 5 and x != 42
+    def g(x):
+        ret = x
+        if f(x):
+            ret += 1
+        else:
+            ret += 2
+        return ret
+    graph, t = get_graph(g, [int])
+    rewire_links(graph)
+    if conftest.option.view:
+        t.view()
+    block = graph.startblock
+    for i in range(3):
+        op = block.exits[False].target.operations[0]
+        assert op.opname == "int_add"
+        assert op.args[1].value == 2
+        block = block.exits[True].target
+    assert block.operations[0].opname == "int_add"
+    assert block.operations[0].args[1].value == 1
+    check_graph(graph, [0], g(0), t)
+    check_graph(graph, [1], g(1), t)
+    check_graph(graph, [5], g(5), t)
+    check_graph(graph, [42], g(42), t)
+
 def test_dont_fold_return():
     def f(x):
         return
@@ -54,10 +87,14 @@
         return 1 + f(x)
     graph, t = get_graph(g, [int])
     constant_folding(graph, t)
+    if conftest.option.view:
+        t.view()
     assert len(graph.startblock.operations) == 0
     check_graph(graph, [1], g(1), t)
 
 def test_constant_fold_call():
+    # fix the logic and try again :-)
+    py.test.skip("constant folding calls is disabled, for sanity reasons")
     def s(x):
         res = 0
         i = 1
@@ -70,6 +107,8 @@
     graph, t = get_graph(g, [int], inline_threshold=0)
     while constant_folding(graph, t):
         pass
+    if conftest.option.view:
+        t.view()
     assert len(graph.startblock.operations) == 1
     check_graph(graph, [10], g(10), t)
 
@@ -86,9 +125,26 @@
     graph, t = get_graph(g, [int])
     partial_folding(graph, t)
     constant_folding(graph, t)
+    if conftest.option.view:
+        t.view()
     assert len(graph.startblock.operations) == 1
     check_graph(graph, [10], g(10), t)
 
+def test_coalesce_links():
+    def f(x):
+        y = 1
+        if x:
+            y += 1
+        else:
+            y += 2
+        return 4
+    graph, t = get_graph(f, [int])
+    simplify.eliminate_empty_blocks(graph)
+    coalesce_links(graph)
+    if conftest.option.view:
+        t.view()
+    assert len(graph.startblock.exits) == 1
+
 def getitem(l, i):  #LookupError, KeyError
     if not isinstance(i, int):
         raise TypeError
@@ -108,6 +164,8 @@
         return 4
     graph, t = get_graph(fn, [int])
     coalesce_links(graph)
+    if conftest.option.view:
+        t.view()
     check_graph(graph, [-1], fn(-1), t)
 
 def list_default_argument(i1, l1=[0]):
@@ -122,3 +180,46 @@
     backend_optimizations(t, propagate=True, ssa_form=False) 
     for i in range(10):
         check_graph(graph, [i], call_list_default_argument(i), t)
+    if conftest.option.view:
+        t.view()
+
+def test_remove_duplicate_casts():
+    class A(object):
+        def __init__(self, x, y):
+            self.x = x
+            self.y = y
+        def getsum(self):
+            return self.x + self.y
+    class B(A):
+        def __init__(self, x, y, z):
+            A.__init__(self, x, y)
+            self.z = z
+        def getsum(self):
+            return self.x + self.y + self.z
+    def f(x, switch):
+        a = A(x, x + 1)
+        b = B(x, x + 1, x + 2)
+        if switch:
+            c = A(x, x + 1)
+        else:
+            c = B(x, x + 1, x + 2)
+        return a.x + a.y + b.x + b.y + b.z + c.getsum()
+    assert f(10, True) == 75
+    graph, t = get_graph(f, [int, bool], 1)
+    num_cast_pointer = len(getops(graph)['cast_pointer'])
+    changed = remove_duplicate_casts(graph, t)
+    assert changed
+    ops = getops(graph)
+    assert len(ops['cast_pointer']) < num_cast_pointer
+    print len(ops['cast_pointer']), num_cast_pointer
+    graph_getsum = graphof(t, B.getsum.im_func)
+    num_cast_pointer = len(getops(graph_getsum)['cast_pointer'])
+    changed = remove_duplicate_casts(graph_getsum, t)
+    assert changed
+    if conftest.option.view:
+        t.view()
+    check_graph(graph, [10, True], 75, t)
+    ops = getops(graph_getsum)
+    assert len(ops['cast_pointer']) < num_cast_pointer
+    print len(ops['cast_pointer']), num_cast_pointer
+    



More information about the Pypy-commit mailing list