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

cfbolz at codespeak.net cfbolz at codespeak.net
Mon Apr 10 01:49:39 CEST 2006


Author: cfbolz
Date: Mon Apr 10 01:49:36 2006
New Revision: 25643

Modified:
   pypy/dist/pypy/translator/backendopt/propagate.py
   pypy/dist/pypy/translator/backendopt/test/test_propagate.py
Log:
some more propagate things:

 * fix bug related to getsubstruct: don't constant-fold it if the result is a
   non-gc-pointer, since the parent might go away

 * new transformation which removes (in some trivial cases) getfield operations.
   this happens if a setfield is followed by a getfield to the same object and
   field or if two getfields are after each other. The decision whether the
   object changed in between is extremely conservative.

 * an (in progress) attempt to propagate constants even more, which depends on
   the DataFlowFamilyBuilder. The approach seems to work, the test doesn't.
   Somehow I suspect a bug in DataFlowFamilyBuilder.



Modified: pypy/dist/pypy/translator/backendopt/propagate.py
==============================================================================
--- pypy/dist/pypy/translator/backendopt/propagate.py	(original)
+++ pypy/dist/pypy/translator/backendopt/propagate.py	Mon Apr 10 01:49:36 2006
@@ -7,6 +7,8 @@
 from pypy.translator.simplify import get_graph
 from pypy.translator.backendopt.removenoops import remove_same_as
 from pypy.translator.backendopt.inline import OP_WEIGHTS
+from pypy.translator.backendopt.ssa import DataFlowFamilyBuilder
+from pypy.translator.backendopt.support import log, var_needsgc
 
 def do_atmost(n, f, *args):
     i = 0
@@ -49,7 +51,7 @@
         link.target = link.target.exits[val].target
         link.args = args
     if candidates:
-        print "rewiring links in graph", graph.name
+        #print "rewiring links in graph", graph.name
         simplify.join_blocks(graph)
         return True
     return False
@@ -75,30 +77,35 @@
 
 def propagate_consts(graph):
     """replace a variable of the inputargs of a block by a constant
-    if all blocks leading to it have the same constant in that position"""
+    if all blocks leading to it have the same constant in that position
+    or if all non-constants that lead to it are in the same variable family
+    as the constant that we go to."""
     entrymap = mkentrymap(graph)
     candidates = []
     changed = False
+    variable_families = DataFlowFamilyBuilder(graph).get_variable_families()
     for block, ingoing in entrymap.iteritems():
         if block in [graph.returnblock, graph.exceptblock]:
             continue
         for i in range(len(ingoing[0].args) - 1, -1, -1):
             vals = {}
+            var = block.inputargs[i]
+            var_rep = variable_families.find_rep(var)
             withvar = True
             for link in ingoing:
                 if isinstance(link.args[i], Variable):
-                    break
+                    if variable_families.find_rep(link.args[i]) != var_rep:
+                        break
                 else:
                     vals[link.args[i]] = True
             else:
-               withvar = False
+                withvar = False
             if len(vals) != 1 or withvar:
                 continue
-            print "propagating constants in graph", graph.name
+            #print "propagating constants in graph", graph.name
             const = vals.keys()[0]
             for link in ingoing:
                 del link.args[i]
-            var = block.inputargs[i]
             del block.inputargs[i]
             op = SpaceOperation("same_as", [const], var)
             block.operations.insert(0, op)
@@ -136,14 +143,17 @@
             if sum([isinstance(arg, Variable) for arg in op.args]):
                 continue
             if lloperation.LL_OPERATIONS[op.opname].canfold:
-                print "folding operation", op, "in graph", graph.name
+                if op.opname in ("getsubstruct", "getarraysubstruct"):
+                    if not var_needsgc(op.result):
+                        continue
                 try:
                     llframe.eval_operation(op)
                 except:
-                    print "did not work"
+                    pass
                 else:
                     res = Constant(llframe.getval(op.result))
-                    print "result", res.value
+                    log.constantfolding("in graph %s, %s = %s" %
+                                        (graph.name, op, res))
                     res.concretetype = op.result.concretetype
                     block.operations[i].opname = "same_as"
                     block.operations[i].args = [res]
@@ -249,6 +259,87 @@
     else:
         return False
 
+def iter_op_pairs(graph, opname1, opname2, equality):
+    for block in graph.iterblocks():
+        num_operations = len(block.operations)
+        for current1 in range(num_operations):
+            if block.operations[current1].opname != opname1:
+                continue
+            op1 = block.operations[current1]
+            for current2 in range(current1 + 1, num_operations):
+                if block.operations[current2].opname != opname2:
+                    continue
+                op2 = block.operations[current2]
+                if equality(op1, op2):
+                    yield block, current1, current2
+    return
+
+def can_be_same(val1, val2):
+    if isinstance(val1, Constant) and isinstance(val2, Constant):
+        return val1.value == val2.value
+    return val1.concretetype == val2.concretetype
+
+def remove_getfield(graph, translator):
+    """ this removes a getfield after a setfield, if they work on the same
+    object and field and if there is no setfield in between which can access
+    the same field"""
+    def equality(op1, op2):
+        if isinstance(op1.args[0], Constant):
+            if isinstance(op2.args[0], Constant):
+                return (op1.args[0].value == op2.args[0].value and
+                        op1.args[1].value == op2.args[1].value)
+            return False
+        return (op1.args[0] == op2.args[0] and
+                op1.args[1].value == op2.args[1].value)
+    def remove_if_possible(block, index1, index2):
+        op1 = block.operations[index1]
+        op2 = block.operations[index2]
+        #print "found"
+        #print op1
+        #print op2
+        fieldname = op1.args[1].value
+        var_or_const = op1.args[0]
+        if op1.opname == "setfield":
+            value = op1.args[2]
+        else:
+            value = op1.result
+        for i in range(index1 + 1, index2):
+            op = block.operations[i]
+            if op.opname == "setfield":
+                if op.args[1].value != fieldname:
+                    continue
+                if can_be_same(op.args[0], op1.args[0]):
+                    break
+            if op.opname == "direct_call":
+                break # giving up for now
+            if op.opname == "indirect_call":
+                break # giving up for now
+        else:
+            op2.opname = "same_as"
+            op2.args = [value]
+            return 1
+        return 0
+    count = 0
+    for block, index1, index2 in iter_op_pairs(
+        graph, "setfield", "getfield", equality):
+        count += remove_if_possible(block, index1, index2)
+    for block, index1, index2 in iter_op_pairs(
+        graph, "getfield", "getfield", equality):
+        count += remove_if_possible(block, index1, index2)
+    if count:
+        remove_same_as(graph)
+    return count
+
+def remove_all_getfields(graph, t):
+    count = 0
+    while 1:
+        newcount = remove_getfield(graph, t)
+        count += newcount
+        if not newcount:
+            break
+    if count:
+        log.removegetfield("removed %s getfields in %s" % (count, graph.name))
+    return count
 
 def propagate_all(translator):
     for graph in translator.graphs:
@@ -260,5 +351,6 @@
             changed = do_atmost(100, constant_folding, graph,
                                            translator) or changed
             changed = partial_folding(graph, translator) or changed
+            changed = remove_all_getfields(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	Mon Apr 10 01:49:36 2006
@@ -81,6 +81,24 @@
     assert len(graph.startblock.exits[0].args) == 1
     check_graph(graph, [1], None, t)
 
+def test_propagate_despite_vars():
+    py.test.skip("immediate test")
+    patterns = [1, 1, 2, 3, 5, 7, 12]
+    class A(object): pass
+    a = A()
+    a.x = 10
+    a.y = 20
+    def f(x):
+        result = 0
+        for i in range(a.x):
+            for j in range(a.y):
+                result += i * j
+        return result
+    graph, t = get_graph(f, [int])
+    propagate_consts(graph)
+    if conftest.option.view:
+        t.view()
+
 def test_constant_fold():
     def f(x):
         return 1
@@ -113,6 +131,20 @@
     assert len(graph.startblock.operations) == 1
     check_graph(graph, [10], g(10), t)
 
+def test_dont_fold_getfield():
+    # must not constant fold this, because the container might be collected
+    string = "blablabla"
+    def f(x):
+        return string[abs(x)]
+    graph, t = get_graph(f, [int])
+    res = constant_folding(graph, t)
+    assert not res
+    if conftest.option.view:
+        t.view()
+    print graph.startblock.operations[1]
+    check_graph(graph, [0], "b", t)
+
+
 def test_fold_const_blocks():
     def s(x):
         res = 0
@@ -184,4 +216,52 @@
     if conftest.option.view:
         t.view()
 
-
+def test_remove_getfield_after_setfield():
+    class A(object):
+        def __init__(self, x=42):
+            self.x = x
+    class B(object):
+        pass
+    global_b = B()
+    global_b.a = None
+    def f(x):
+        a = A(x)
+        global_b.a = a
+        global_b.a.x += 1
+        return global_b.a.x
+    graph, t = get_graph(f, [int], all_opts=False)
+    assert len(graph.startblock.operations) == 11
+    count = remove_all_getfields(graph, t)
+    if conftest.option.view:
+        t.view()
+    assert len(graph.startblock.operations) == 8
+    check_graph(graph, [42], 43, t)
+
+def test_remove_getfield_after_getfield():
+    class A(object):
+        def __init__(self, x=42):
+            self.x = x
+    class B(object):
+        def __init__(self, a):
+            self.a = a
+    class C:
+        pass
+    global_c = C()
+    global_c.b1 = None
+    global_c.b2 = None
+    def f(x):
+        a = A(x)
+        b = B(a)
+        global_c.b1 = b
+        a1 = global_c.b1.a
+        global_c.b2 = B(a1)
+        return global_c.b1.a.x
+    graph, t = get_graph(f, [int])
+    assert len(graph.startblock.operations) == 23
+    count = remove_all_getfields(graph, t)
+    assert count
+    if conftest.option.view:
+        t.view()
+    assert len(graph.startblock.operations) == 20
+    check_graph(graph, [42], 42, t)
+    



More information about the Pypy-commit mailing list