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

cfbolz at codespeak.net cfbolz at codespeak.net
Tue Sep 27 00:55:56 CEST 2005


Author: cfbolz
Date: Tue Sep 27 00:55:54 2005
New Revision: 17891

Added:
   pypy/dist/pypy/translator/backendopt/propagate.py
   pypy/dist/pypy/translator/backendopt/test/test_propagate.py
Modified:
   pypy/dist/pypy/translator/backendopt/all.py
Log:
Some more "I am on vacation and have way too much time" graph transformations:
these try to do some amount of constant folding and constant propagation. They
are nice in theory although I think rather unimportant in practice (they yield
8% speedup on targetrpystone).


Modified: pypy/dist/pypy/translator/backendopt/all.py
==============================================================================
--- pypy/dist/pypy/translator/backendopt/all.py	(original)
+++ pypy/dist/pypy/translator/backendopt/all.py	Tue Sep 27 00:55:54 2005
@@ -3,21 +3,24 @@
 from pypy.translator.backendopt.inline import auto_inlining
 from pypy.translator.backendopt.malloc import remove_simple_mallocs
 from pypy.translator.backendopt.ssa import SSI_to_SSA
+from pypy.translator.backendopt.propagate import propagate_all
 from pypy.translator import simplify
 
 
 def backend_optimizations(translator, inline_threshold=1,
                                       mallocs=True,
-                                      ssa_form=True):
+                                      ssa_form=True,
+                                      propagate=True):
     # remove obvious no-ops
     for graph in translator.flowgraphs.values():
         remove_same_as(graph)
         simplify.eliminate_empty_blocks(graph)
-
+        simplify.transform_dead_op_vars(graph, translator)
     # inline functions in each other
+    if propagate:
+        propagate_all(translator)
     if inline_threshold:
         auto_inlining(translator, inline_threshold)
-
     # vaporize mallocs
     if mallocs:
         for graph in translator.flowgraphs.values():
@@ -25,8 +28,10 @@
                 # remove typical leftovers from malloc removal
                 remove_same_as(graph)
                 simplify.eliminate_empty_blocks(graph)
-                simplify.transform_dead_op_vars(graph)
-
+                simplify.transform_dead_op_vars(graph, translator)
+    if propagate:
+        propagate_all(translator)
+   
     if ssa_form:
         for graph in translator.flowgraphs.values():
             SSI_to_SSA(graph)

Added: pypy/dist/pypy/translator/backendopt/propagate.py
==============================================================================
--- (empty file)
+++ pypy/dist/pypy/translator/backendopt/propagate.py	Tue Sep 27 00:55:54 2005
@@ -0,0 +1,276 @@
+from pypy.objspace.flow.model import Block, Variable, Constant, last_exception
+from pypy.objspace.flow.model import traverse, mkentrymap, checkgraph
+from pypy.rpython.lltype import Void, Bool
+from pypy.rpython.llinterp import LLInterpreter, LLFrame
+from pypy.translator import simplify
+from pypy.translator.backendopt.tailrecursion import get_graph
+from pypy.translator.backendopt.removenoops import remove_same_as
+
+def do_atmost(n, f, *args):
+    i = 0
+    while f(*args):
+        i += 1
+        if i > n:
+            break
+    return i > 0
+
+def rewire_links(graph):
+    """This function changes the target of certain links: this happens
+    if the exitswitch is passed along the link to another block where
+    it is again used as the exitswitch. This situation occurs after the
+    inlining of functions that return a bool."""
+    entrymap = mkentrymap(graph)
+    candidates = {}
+    def visit(block):
+        if not isinstance(block, Block):
+            return
+        if (isinstance(block.exitswitch, Variable) and
+            block.exitswitch.concretetype is Bool):
+            for val, link in enumerate(block.exits):
+                val = bool(val)
+                if block.exitswitch not in link.args:
+                    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 = []
+        for arg in link.target.exits[val].args:
+            if isinstance(arg, Constant):
+                args.append(arg)
+            else:
+                index = link.target.inputargs.index(arg)
+                args.append(link.args[index])
+        link.target = link.target.exits[val].target
+        link.args = args
+    if candidates:
+        print "rewiring links in graph", graph.name
+        checkgraph(graph)
+        return True
+    return False
+
+def coalesce_links(graph):
+    candidates = {}
+    def visit(block):
+        if not isinstance(block, Block):
+            return
+        if len(block.exits) != 2:
+            return
+        if (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:
+        block.exitswitch = None
+        block.exits = block.exits[:1]
+        block.exits[0].exitcase = None
+    if candidates:
+        print "coalescing links in graph", graph.name
+        return True
+    else:
+        return False
+
+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"""
+    entrymap = mkentrymap(graph)
+    candidates = []
+    changed = [False]
+    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 = {}
+            withvar = True
+            for link in ingoing:
+                if isinstance(link.args[i], Variable):
+                    break
+                else:
+                    vals[link.args[i]] = True
+            else:
+               withvar = False
+            if len(vals) != 1 or withvar:
+                continue
+            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]
+            block.renamevariables({var: const})
+            changed[0] = True
+    if changed[0]:
+        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 cast_pointer""".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
+
+class CountingLLFrame(LLFrame):
+    def __init__(self, graph, args, llinterpreter, f_back=None, maxcount=1000):
+        super(CountingLLFrame, self).__init__(graph, args, llinterpreter, f_back)
+        self.count = 0
+        self.maxcount = maxcount
+
+    def eval_operation(self, operation):
+        if operation is None: #can happen in the middle of constant folding
+            return
+        self.count += 1
+        if self.count > self.maxcount:
+            raise TooManyOperations
+        return super(CountingLLFrame, self).eval_operation(operation)
+
+def constant_folding(graph, translator):
+    """do constant folding if the arguments of an operations are constants"""
+    lli = LLInterpreter(translator.flowgraphs, translator.rtyper)
+    llframe = LLFrame(graph, None, lli)
+    changed = [False]
+    def visit(block):
+        if not isinstance(block, Block):
+            return
+        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:
+                print "folding operation", op, "in graph", graph.name
+                try:
+                    llframe.eval_operation(op)
+                except:
+                    print "did not work"
+                else:
+                    res = Constant(llframe.getval(op.result))
+                    print "result", res.value
+                    res.concretetype = op.result.concretetype
+                    block.operations[i].opname = "same_as"
+                    block.operations[i].args = [res]
+                    changed[0] = True
+            elif 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
+                    (block.exitswitch != Constant(last_exception) or 
+                     i != len(block.operations) - 1)):
+                    args = [arg.value for arg in op.args[1:]]
+                    countingframe = CountingLLFrame(called_graph, args, lli)
+                    print "folding call", op, "in graph", graph.name
+                    try:
+                        res = countingframe.eval()
+                    except:
+                        print "did not work"
+                        pass
+                    else:
+                        print "result", res
+                        res = Constant(res)
+                        res.concretetype = op.result.concretetype
+                        block.operations[i].opname = "same_as"
+                        block.operations[i].args = [res]
+                        changed[0] = True
+        block.operations = [op for op in block.operations if op is not None]
+    traverse(visit, graph)
+    if changed[0]:
+        remove_same_as(graph)
+        propagate_consts(graph)
+        checkgraph(graph)
+        return True
+    return False
+
+def partial_folding_once(graph, translator):
+    lli = LLInterpreter(translator.flowgraphs, translator.rtyper)
+    entrymap = mkentrymap(graph)
+    def visit(block):
+        if (not isinstance(block, Block) or block is graph.startblock or
+            block is graph.returnblock or block is graph.exceptblock):
+            return
+        usedvars = {}
+        for op in block.operations:
+            if op.opname in cannot_constant_fold:
+                return
+            for arg in op.args:
+                if (isinstance(arg, Variable) and arg in block.inputargs):
+                    usedvars[arg] = True
+        if isinstance(block.exitswitch, Variable):
+            usedvars[block.exitswitch] = True
+        pattern = [arg in usedvars for arg in block.inputargs]
+        for link in entrymap[block]:
+            s = sum([isinstance(arg, Constant) or not p
+                         for arg, p in zip(link.args, pattern)])
+            if s != len(link.args):
+                continue
+            args = []
+            for i, arg in enumerate(link.args):
+                if isinstance(arg, Constant):
+                    args.append(arg.value)
+                else:
+                    assert not pattern[i]
+                    args.append(arg.concretetype._example())
+            llframe = LLFrame(graph, None, lli)
+            llframe.fillvars(block, args)
+            nextblock, forwardargs = llframe.eval_block(block)
+            if nextblock is not None:
+                newargs = []
+                for i, arg in enumerate(nextblock.inputargs):
+                    try:
+                        index = [l.target for l in block.exits].index(nextblock)
+                        index = block.inputargs.index(block.exits[index].args[i])
+                    except ValueError:
+                        c = Constant(forwardargs[i])
+                        c.concretetype = arg.concretetype
+                        newargs.append(c)
+                    else:
+                        newargs.append(link.args[index])
+            else:
+                assert 0, "this should not occur"
+            unchanged = link.target == nextblock and link.args == newargs
+            link.target = nextblock
+            link.args = newargs
+            checkgraph(graph)
+            if not unchanged:
+                raise ValueError
+    try:
+        traverse(visit, graph)
+    except ValueError:
+        return True
+    else:
+        return False
+
+def partial_folding(graph, translator):
+    """this function does constant folding in the following situation:
+    a block has a link that leads to it that has only constant args. Then all
+    the operations of this block are evaluated and the link leading to the
+    block is adjusted according to the resulting value of the exitswitch"""
+    if do_atmost(1000, partial_folding_once, graph, translator):
+        propagate_consts(graph)
+        simplify.join_blocks(graph)
+        return True
+    else:
+        return False
+
+def propagate_all(translator):
+    for graph in translator.flowgraphs.itervalues():
+        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)
+            return changed
+        do_atmost(10, prop)

Added: pypy/dist/pypy/translator/backendopt/test/test_propagate.py
==============================================================================
--- (empty file)
+++ pypy/dist/pypy/translator/backendopt/test/test_propagate.py	Tue Sep 27 00:55:54 2005
@@ -0,0 +1,87 @@
+from pypy.translator.translator import Translator
+from pypy.translator.backendopt.propagate import *
+from pypy.rpython.llinterp import LLInterpreter
+
+
+def get_graph(fn, signature):
+    t = Translator(fn)
+    t.annotate(signature)
+    t.specialize()
+    t.backend_optimizations(ssa_form=False, propagate=False) 
+    graph = t.getflowgraph()
+    return graph, t
+
+def check_graph(graph, args, expected_result, t):
+    interp = LLInterpreter(t.flowgraphs, t.rtyper)
+    res = interp.eval_function(None, args, graph=graph)
+    assert res == expected_result
+
+def check_get_graph(fn, signature, args, expected_result):
+    graph, t = get_graph(fn, signature)
+    check_graph(graph, args, expected_result, t)
+    return graph
+
+def test_inline_and():
+    def f(x):
+        return x != 1 and x != 5 and x != 42
+    def g(x):
+        ret = 0
+        for i in range(x):
+            if f(x):
+                ret += x
+            else:
+                ret += x + 1
+        return ret
+    graph, t = get_graph(g, [int])
+    propagate_consts(graph)
+    assert len(graph.startblock.exits[0].args) == 4
+    check_graph(graph, [100], g(100), t)
+    
+def test_dont_fold_return():
+    def f(x):
+        return
+    graph, t = get_graph(f, [int])
+    propagate_consts(graph)
+    assert len(graph.startblock.exits[0].args) == 1
+    check_graph(graph, [1], None, t)
+
+def test_constant_fold():
+    def f(x):
+        return 1
+    def g(x):
+        return 1 + f(x)
+    graph, t = get_graph(g, [int])
+    constant_folding(graph, t)
+    assert len(graph.startblock.operations) == 0
+    check_graph(graph, [1], g(1), t)
+
+def test_constant_fold_call():
+    def s(x):
+        res = 0
+        for i in range(1, x + 1):
+            res += i
+        return res
+    def g(x):
+        return s(100) + s(1) + x
+    graph, t = get_graph(g, [int])
+    while constant_folding(graph, t):
+        pass
+    assert len(graph.startblock.operations) == 1
+    check_graph(graph, [10], g(10), t)
+
+def test_fold_const_blocks():
+    def s(x):
+        res = 0
+        i = 1
+        while i < x:
+            res += i
+            i += 1
+        return res
+    def g(x):
+        return s(100) + s(99) + x
+    graph, t = get_graph(g, [int])
+    partial_folding(graph, t)
+    constant_folding(graph, t)
+    assert len(graph.startblock.operations) == 1
+    check_graph(graph, [10], g(10), t)
+



More information about the Pypy-commit mailing list