[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