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

arigo at codespeak.net arigo at codespeak.net
Sat Sep 24 19:59:59 CEST 2005


Author: arigo
Date: Sat Sep 24 19:59:53 2005
New Revision: 17837

Modified:
   pypy/dist/pypy/objspace/flow/model.py
   pypy/dist/pypy/translator/backendopt/ssa.py
   pypy/dist/pypy/translator/simplify.py
Log:
(pedronis, arigo)

* rewrote remove_identical_vars().  The algorithm is somehow getting
  clearer, though the code doesn't necessary shows this.  Well, the
  comments help, maybe.

* added FunctionGraph.iterblocks() and FunctionGraph.iterlinks(),
  which are actually not used by the rest of this check-in.



Modified: pypy/dist/pypy/objspace/flow/model.py
==============================================================================
--- pypy/dist/pypy/objspace/flow/model.py	(original)
+++ pypy/dist/pypy/objspace/flow/model.py	Sat Sep 24 19:59:53 2005
@@ -68,21 +68,27 @@
         return getsource(self.func)
     source = roproperty(getsource)
 
-##    def hasonlyexceptionreturns(self):
-##        try:
-##            return self._onlyex
-##        except AttributeError: 
-##            def visit(link):
-##                if isinstance(link, Link):
-##                    if link.target == self.returnblock: 
-##                        raise ValueError(link) 
-##            try:
-##                traverse(visit, self)
-##            except ValueError:
-##                self._onlyex = False 
-##            else:
-##                self._onlyex = True
-##            return self._onlyex 
+    def iterblocks(self):
+        pending = [self.startblock]
+        seen = {id(self.startblock): True}
+        for block in pending:
+            yield block
+            for link in block.exits:
+                targetid = id(link.target)
+                if targetid not in seen:
+                    pending.append(link.target)
+                    seen[targetid] = True
+
+    def iterlinks(self):
+        pending = [self.startblock]
+        seen = {id(self.startblock): True}
+        for block in pending:
+            for link in block.exits:
+                yield link
+                targetid = id(link.target)
+                if targetid not in seen:
+                    pending.append(link.target)
+                    seen[targetid] = True
 
     def show(self):
         from pypy.translator.tool.graphpage import SingleGraphPage

Modified: pypy/dist/pypy/translator/backendopt/ssa.py
==============================================================================
--- pypy/dist/pypy/translator/backendopt/ssa.py	(original)
+++ pypy/dist/pypy/translator/backendopt/ssa.py	Sat Sep 24 19:59:53 2005
@@ -1,55 +1,91 @@
 from pypy.objspace.flow.model import Variable, mkentrymap, flatten, Block
 from pypy.tool.unionfind import UnionFind
 
-def data_flow_families(graph):
-    """Follow the flow of the data in the graph.  Returns a UnionFind grouping
+class DataFlowFamilyBuilder:
+    """Follow the flow of the data in the graph.  Builds a UnionFind grouping
     all the variables by families: each family contains exactly one variable
     where a value is stored into -- either by an operation or a merge -- and
     all following variables where the value is just passed unmerged into the
     next block.
     """
 
-    # Build a list of "unification opportunities": for each block and each 'n',
-    # an "opportunity" is the list of the block's nth input variable plus
-    # the nth output variable from each of the incoming links.
-    opportunities = []
-    for block, links in mkentrymap(graph).items():
-        if block is graph.startblock:
-            continue
-        assert links
-        for n, inputvar in enumerate(block.inputargs):
-            vars = [inputvar]
-            for link in links:
-                var = link.args[n]
-                if not isinstance(var, Variable):
-                    break
-                vars.append(var)
-            else:
-                # if no Constant found in the incoming links
-                opportunities.append(vars)
-
-    # An "opportunitiy" that lists exactly two distinct variables means that
-    # the two variables can be unified.  We maintain the unification status in
-    # 'variable_families'.  When variables are unified, it might reduce the
-    # number of distinct variables and thus open other "opportunities" for
-    # unification.
-    progress = True
-    variable_families = UnionFind()
-    while progress:
-        progress = False
-        pending_opportunities = []
-        for vars in opportunities:
-            repvars = [variable_families.find_rep(v1) for v1 in vars]
-            repvars = dict.fromkeys(repvars).keys()
-            if len(repvars) > 2:
-                # cannot unify now, but maybe later?
-                pending_opportunities.append(repvars)
-            elif len(repvars) == 2:
-                # unify!
-                variable_families.union(*repvars)
-                progress = True
-        opportunities = pending_opportunities
-    return variable_families
+    def __init__(self, graph):
+        # Build a list of "unification opportunities": for each block and each
+        # 'n', an "opportunity" groups the block's nth input variable with
+        # the nth output variable from each of the incoming links, in a list:
+        # [Block, blockvar, linkvar, linkvar, linkvar...]
+        opportunities = []
+        for block, links in mkentrymap(graph).items():
+            if block is graph.startblock:
+                continue
+            assert links
+            for n, inputvar in enumerate(block.inputargs):
+                vars = [block, inputvar]
+                for link in links:
+                    var = link.args[n]
+                    if not isinstance(var, Variable):
+                        break
+                    vars.append(var)
+                else:
+                    # if no Constant found in the incoming links
+                    opportunities.append(vars)
+        self.opportunities = opportunities
+        self.variable_families = UnionFind()
+
+    def complete(self):
+        # An "opportunitiy" that lists exactly two distinct variables means that
+        # the two variables can be unified.  We maintain the unification status
+        # in 'variable_families'.  When variables are unified, it might reduce
+        # the number of distinct variables and thus open other "opportunities"
+        # for unification.
+        variable_families = self.variable_families
+        any_progress_at_all = False
+        progress = True
+        while progress:
+            progress = False
+            pending_opportunities = []
+            for vars in self.opportunities:
+                repvars = [variable_families.find_rep(v1) for v1 in vars[1:]]
+                repvars_without_duplicates = dict.fromkeys(repvars)
+                count = len(repvars_without_duplicates)
+                if count > 2:
+                    # cannot unify now, but maybe later?
+                    pending_opportunities.append(vars[:1] + repvars)
+                elif count == 2:
+                    # unify!
+                    variable_families.union(*repvars_without_duplicates)
+                    progress = True
+            self.opportunities = pending_opportunities
+            any_progress_at_all |= progress
+        return any_progress_at_all
+
+    def merge_identical_phi_nodes(self):
+        variable_families = self.variable_families
+        any_progress_at_all = False
+        progress = True
+        while progress:
+            progress = False
+            block_phi_nodes = {}   # in the SSA sense
+            for vars in self.opportunities:
+                block, blockvar = vars[:2]
+                linksvars = vars[2:]   # from the incoming links
+                linksvars = [variable_families.find_rep(v) for v in linksvars]
+                phi_node = (block,) + tuple(linksvars) # ignoring n and blockvar
+                if phi_node in block_phi_nodes:
+                    # already seen: we have two phi nodes in the same block that
+                    # get exactly the same incoming vars.  Identify the results.
+                    blockvar1 = block_phi_nodes[phi_node]
+                    if variable_families.union(blockvar1, blockvar)[0]:
+                        progress = True
+                else:
+                    block_phi_nodes[phi_node] = blockvar
+            any_progress_at_all |= progress
+        return any_progress_at_all
+
+    def get_variable_families(self):
+        self.complete()
+        return self.variable_families
+
 
 def SSI_to_SSA(graph):
     """Rename the variables in a flow graph as much as possible without
@@ -61,7 +97,7 @@
     result of an operation only once in the whole graph, but it can be
     passed to other blocks across links.
     """
-    variable_families = data_flow_families(graph)
+    variable_families = DataFlowFamilyBuilder(graph).get_variable_families()
     # rename variables to give them the name of their familiy representant
     for v in variable_families.keys():
         v1 = variable_families.find_rep(v)

Modified: pypy/dist/pypy/translator/simplify.py
==============================================================================
--- pypy/dist/pypy/translator/simplify.py	(original)
+++ pypy/dist/pypy/translator/simplify.py	Sat Sep 24 19:59:53 2005
@@ -453,44 +453,59 @@
     which otherwise doesn't realize that tests performed on one of the copies
     of the variable also affect the other."""
 
-    from pypy.translator.backendopt.ssa import data_flow_families
-    entrymapitems = mkentrymap(graph).items()
-    progress = True
-    while progress:
-        variable_families = data_flow_families(graph)
-        progress = False
-        for block, links in entrymapitems:
-            if not block.exits:
-                continue
-            entryargs = {}
-            for i in range(len(block.inputargs)):
-                # list of possible vars that can arrive in i'th position
-                key = []
+    # This algorithm is based on DataFlowFamilyBuilder, used as a
+    # "phi node remover" (in the SSA sense).  'variable_families' is a
+    # UnionFind object that groups variables by families; variables from the
+    # same family can be identified, and if two input arguments of a block
+    # end up in the same family, then we really remove one of them in favor
+    # of the other.
+    #
+    # The idea is to identify as much variables as possible by trying
+    # iteratively two kinds of phi node removal:
+    #
+    #  * "vertical", by identifying variables from different blocks, when
+    #    we see that a value just flows unmodified into the next block without
+    #    needing any merge (this is what backendopt.ssa.SSI_to_SSA() would do
+    #    as well);
+    #
+    #  * "horizontal", by identifying two input variables of the same block,
+    #    when these two variables' phi nodes have the same argument -- i.e.
+    #    when for all possible incoming paths they would get twice the same
+    #    value (this is really the purpose of remove_identical_vars()).
+    #
+    from pypy.translator.backendopt.ssa import DataFlowFamilyBuilder
+    builder = DataFlowFamilyBuilder(graph)
+    variable_families = builder.get_variable_families()  # vertical removal
+    while True:
+        if not builder.merge_identical_phi_nodes():    # horizontal removal
+            break
+        if not builder.complete():                     # vertical removal
+            break
+
+    for block, links in mkentrymap(graph).items():
+        if block is graph.startblock:
+            continue
+        renaming = {}
+        family2blockvar = {}
+        kills = []
+        for i, v in enumerate(block.inputargs):
+            v1 = variable_families.find_rep(v)
+            if v1 in family2blockvar:
+                # already seen -- this variable can be shared with the
+                # previous one
+                renaming[v] = family2blockvar[v1]
+                kills.append(i)
+            else:
+                family2blockvar[v1] = v
+        if renaming:
+            block.renamevariables(renaming)
+            # remove the now-duplicate input variables
+            kills.reverse()   # starting from the end
+            for i in kills:
+                del block.inputargs[i]
                 for link in links:
-                    v = link.args[i]
-                    if isinstance(v, Constant):
-                        break
-                    key.append(variable_families.find_rep(v))
-                else: # if no Constant
-                    key = tuple(key)
-                    if key not in entryargs:
-                        entryargs[key] = i
-                    else:
-                        j = entryargs[key]
-                        # positions i and j receive exactly the same input
-                        # vars, we can remove the argument i and replace it
-                        # with the j.
-                        argi = block.inputargs[i]
-                        argj = block.inputargs[j]
-                        block.renamevariables({argi: argj})
-                        assert block.inputargs[i] == block.inputargs[j]== argj
-                        del block.inputargs[i]
-                        for link in links:
-                            assert (variable_families.find_rep(link.args[i])==
-                                    variable_families.find_rep(link.args[j]))
-                            del link.args[i]
-                        progress = True
-                        break   # block.inputargs mutated
+                    del link.args[i]
+
 
 def coalesce_is_true(graph):
     """coalesce paths that go through an is_true and a directly successive



More information about the Pypy-commit mailing list