[pypysvn] 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 checkin.
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 nowduplicate 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 Pypycommit
mailing list