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

cfbolz at codespeak.net cfbolz at codespeak.net
Tue Sep 27 00:43:41 CEST 2005


Author: cfbolz
Date: Tue Sep 27 00:43:40 2005
New Revision: 17890

Added:
   pypy/dist/pypy/translator/test/test_simplify.py
Modified:
   pypy/dist/pypy/translator/simplify.py
Log:
try to find functions without side-effects and remove them in
transform_dead_op_vars.


Modified: pypy/dist/pypy/translator/simplify.py
==============================================================================
--- pypy/dist/pypy/translator/simplify.py	(original)
+++ pypy/dist/pypy/translator/simplify.py	Tue Sep 27 00:43:40 2005
@@ -9,7 +9,7 @@
 from pypy.objspace.flow.model import Variable, Constant, Block, Link
 from pypy.objspace.flow.model import last_exception
 from pypy.objspace.flow.model import checkgraph, traverse, mkentrymap
-
+from pypy.translator.backendopt.tailrecursion import get_graph
 # ____________________________________________________________
 
 def eliminate_empty_blocks(graph):
@@ -322,7 +322,57 @@
                 block.exits = tuple(lst)
     traverse(visit, graph)
 
-def transform_dead_op_vars(graph):
+
+# _____________________________________________________________________
+# decide whether a function has side effects
+lloperations_with_side_effects = {"setfield": True,
+                                  "setarrayitem": True,
+                                 }
+
+class HasSideEffects(Exception):
+    pass
+
+# XXX: this could even be improved:
+# if setfield and setarrayitem only occur on things that are malloced
+# in this function then the function still does not have side effects
+
+def has_no_side_effects(translator, graph, seen=None):
+    #is the graph specialized? if no we can't say anything
+    #don't cache the result though
+    if translator.rtyper is None:
+        return False
+    else:
+        if graph.startblock not in translator.rtyper.already_seen:
+            return False
+    if seen is None:
+        seen = []
+    elif graph in seen:
+        return True
+    try:
+        def visit(block):
+            if not isinstance(block, Block):
+                return
+            for op in block.operations:
+                if op.opname in lloperations_with_side_effects:
+                    raise HasSideEffects
+                if op.opname == "direct_call":
+                    if isinstance(op.args[0], Variable):
+                        raise HasSideEffects
+                    g = get_graph(op.args[0], translator)
+                    if g is None:
+                        raise HasSideEffects
+                    if not has_no_side_effects(translator, g, seen + [graph]):
+                        raise HasSideEffects
+        traverse(visit, graph)
+    except HasSideEffects:
+        return False
+    else:
+        return True
+
+# ___________________________________________________________________________
+# remove operations if their result is not used and they have no side effects
+
+def transform_dead_op_vars(graph, translator=None):
     """Remove dead operations and variables that are passed over a link
     but not used in the target block. Input is a graph."""
     blocks = {}
@@ -330,7 +380,7 @@
         if isinstance(block, Block):
             blocks[block] = True
     traverse(visit, graph)
-    return transform_dead_op_vars_in_blocks(blocks)
+    return transform_dead_op_vars_in_blocks(blocks, translator)
 
 # the set of operations that can safely be removed
 # (they have no side effects, at least in R-Python)
@@ -349,7 +399,7 @@
     hasattr: True,
     }
 
-def transform_dead_op_vars_in_blocks(blocks):
+def transform_dead_op_vars_in_blocks(blocks, translator=None):
     """Remove dead operations and variables that are passed over a link
     but not used in the target block. Input is a set of blocks"""
     read_vars = {}  # set of variables really used
@@ -427,7 +477,14 @@
                                 del block.operations[i]
                         except TypeError:   # func is not hashable
                             pass
-
+                elif op.opname == 'direct_call':
+                    if translator is not None:
+                        graph = get_graph(op.args[0], translator)
+                        if (graph is not None and
+                            has_no_side_effects(translator, graph) and
+                            (block.exitswitch != Constant(last_exception) or
+                             i != len(block.operations)- 1)):
+                            del block.operations[i]
         # look for output variables never used
         # warning: this must be completely done *before* we attempt to
         # remove the corresponding variables from block.inputargs!

Added: pypy/dist/pypy/translator/test/test_simplify.py
==============================================================================
--- (empty file)
+++ pypy/dist/pypy/translator/test/test_simplify.py	Tue Sep 27 00:43:40 2005
@@ -0,0 +1,59 @@
+from pypy.translator.translator import Translator
+from pypy.objspace.flow.model import traverse, Block
+
+def test_remove_direct_call_without_side_effects():
+    def f(x):
+        return x + 123
+    def g(x):
+        a = f(x)
+        return x * 12
+    t = Translator(g)
+    a = t.annotate([int])
+    t.specialize()
+    t.backend_optimizations()
+    assert len(t.flowgraphs[g].startblock.operations) == 1
+
+def test_dont_remove_external_calls():
+    import os
+    def f(x):
+        os.close(x)
+    t = Translator(f)
+    a = t.annotate([int])
+    t.specialize()
+    t.backend_optimizations()
+    assert len(t.flowgraphs[f].startblock.operations) == 1
+
+def test_remove_recursive_call():
+    def rec(a):
+        if a <= 1:block.exitswitch != Constant(last_exception):
+            return 0
+        else:
+            return rec(a - 1) + 1
+    def f(x):
+        a = rec(x)
+        return x + 12
+    t = Translator(f)
+    a = t.annotate([int])
+    t.specialize()
+    t.backend_optimizations()
+    assert len(t.flowgraphs[f].startblock.operations)
+
+def test_dont_remove_if_exception_guarded():
+    def f(x):
+        a = {} #do some stuff to prevent inlining
+        a['123'] = 123
+        a['1123'] = 1234
+        return x + 1
+    def g(x):
+        try:
+            a = f(x)
+        except OverflowError:
+            raise
+        else:
+            return 1
+    t = Translator(g)
+    a = t.annotate([int])
+    t.specialize()
+    t.backend_optimizations()
+    assert t.flowgraphs[g].startblock.operations[-1].opname == 'direct_call'
+



More information about the Pypy-commit mailing list