[pypy-svn] r68723 - in pypy/branch/effect-analysis/pypy/translator/backendopt/effect: . test
verte at codespeak.net
verte at codespeak.net
Sat Oct 24 05:18:04 CEST 2009
Author: verte
Date: Sat Oct 24 05:18:03 2009
New Revision: 68723
Added:
pypy/branch/effect-analysis/pypy/translator/backendopt/effect/
pypy/branch/effect-analysis/pypy/translator/backendopt/effect/__init__.py
pypy/branch/effect-analysis/pypy/translator/backendopt/effect/model.py
pypy/branch/effect-analysis/pypy/translator/backendopt/effect/test/
pypy/branch/effect-analysis/pypy/translator/backendopt/effect/test/__init__.py
pypy/branch/effect-analysis/pypy/translator/backendopt/effect/test/test_model.py
Log:
Initial commit, mostly just failing tests.
Added: pypy/branch/effect-analysis/pypy/translator/backendopt/effect/__init__.py
==============================================================================
Added: pypy/branch/effect-analysis/pypy/translator/backendopt/effect/model.py
==============================================================================
--- (empty file)
+++ pypy/branch/effect-analysis/pypy/translator/backendopt/effect/model.py Sat Oct 24 05:18:03 2009
@@ -0,0 +1,252 @@
+from collections import defaultdict
+
+ITEM = object()
+
+def namer():
+ from itertools import count
+ num = count()
+ def make_name():
+ for i in num:
+ return '%%r%x' % (i,)
+ return make_name
+
+make_name = namer()
+
+
+def index_name_maker(context, name):
+ def aggregate_factory():
+ return AggregateRegion(context, '%s[]' % (name,))
+ return aggregate_factory
+
+
+class Region(object):
+ def __init__(self, context=None, name=None):
+ self.context = context
+ if name is not None:
+ self.name = name
+ else:
+ self.name = make_name()
+ self.indexes = defaultdict(index_name_maker(context, self.name))
+
+ def get_item(self, index=ITEM):
+ return self.indexes[index]
+
+ def set_item(self, value, index=ITEM):
+ self.indexes[index].add(value)
+
+ def is_concrete(self, context):
+ return True
+
+ def is_abstract(self):
+ return False
+
+ def contents(self):
+ return ()
+
+ def __repr__(self):
+ return self.name
+
+
+class AbstractRegion(Region):
+ def inside(self, context):
+ # if the context is a child of the one this is on, we are already in
+ # that context
+ value = context.lookup_abstract_value(self)
+
+ if value is not None:
+ return value
+
+ return self
+
+ def is_concrete(self, context):
+ value = context.lookup_abstract_value(self)
+ return value is not None
+
+ def contents(self):
+ if self.context is not None:
+ return [self.context.get_abstract_value(self)]
+ return ()
+
+
+
+class ConcreteRegion(Region):
+ """A region refering to a known constant.
+
+ Occasionally we know beforehand the entire value that the region
+ will take, in the case of global constants and what not.
+ """
+
+
+class SemiConcreteRegion(Region):
+ """A region referring to an object created on the domain of
+ interest.
+
+ A semi-concrete region is one that we know most of its secrets.
+ Typically, a semi-concrete region will refer to all instances of
+ an object created at a certain point. We then use the call
+ context to distinguish various paths an object can take.
+
+ In this sense a region is semi-concrete; that together with a
+ context, it can stand on its own and be absolutely disambiguated.
+ """
+
+
+class AggregateRegion(Region):
+ def __init__(self, *args, **kwargs):
+ self.regions = set()
+ super(AggregateRegion, self).__init__(*args, **kwargs)
+
+ def is_concrete(self, context):
+ return False
+
+ def contents(self):
+ return self.regions
+
+ def add(self, region):
+ if region in self.regions:
+ return
+ self.regions.add(region)
+ for index, value in self.indexes.iteritems():
+ value.add(region.get_item(index))
+
+ def get_item(self, index=ITEM):
+ return super(AggregateRegion, self).get_item(index)
+
+ def set_item(self, value, index=ITEM):
+ super(AggregateRegion, self).set_item(value, index)
+
+
+def distinct(x, y):
+ return False
+
+
+# XXX: use .contents() instead of an ugly type check
+def separate_aggregates(regions):
+ direct = set([r for r in regions if not isinstance(r, AggregateRegion)])
+ refs = set([r for r in regions if isinstance(r, AggregateRegion)])
+ return direct, refs
+
+
+def flatten(regions):
+ """Flatten a set of regions.
+
+ Dataflow graphs typically generate AggregateRegions that refer to more
+ AggregateRegions. flatten takes a set of regions and snaps pointers to
+ others, and removes self-references.
+ """
+
+ # Unfortunately suboptimal in the case of higher-order cycles.
+ # To be addressed.
+
+ for region in regions:
+ if not isinstance(region, AggregateRegion):
+ continue
+ if not any(isinstance(r, AggregateRegion) for r in region.regions):
+ continue
+
+ print '\nRegion:', region
+ # region.regions will be built as a set containing only non-
+ # aggregate references.
+ #
+ # refs_working is a set of references we have yet to deal with.
+ #
+ # refs_inlined is the set of references that have already been
+ # dealt with.
+ #
+ # self_refs is a subset of refs_inlined that contains those that
+ # are equal to this set.
+
+ region.regions, refs_working = separate_aggregates(region.regions)
+ refs_inlined = set([region])
+ self_refs = set([region])
+
+ while True:
+ reference = refs_working.pop()
+ refs_inlined.add(reference)
+ new_regions, new_refs = separate_aggregates(reference.regions)
+ if self_refs & reference.regions:
+ # these sets contain each other => they are equal
+ self_refs.add(reference)
+ reference.regions = region.regions
+ region.regions.update(new_regions)
+ refs_working.update(new_refs - refs_inlined)
+ if not refs_working:
+ break
+
+
+
+class RegionContext(object):
+ """Distinguish concrete regions across call boundaries.
+
+ if we had:
+
+ def newlist():
+ return []
+
+ we would assign a semi concrete region to the new list so we could
+ identify its origin. However, that would not distinguish:
+
+ def f():
+ x = newlist()
+ y = newlist()
+ x.append(someregion)
+ return y
+
+ The region of the return value of f would be seen to be modified.
+ So, we imbue function calls with a 'context', which will
+ disabmiguate between these cases.
+
+ Contexts can also distinguish usage across function call and loop
+ boundaries, tightening the analysis further. However, we do not
+ track function applications yet and don't handle loops specially.
+ """
+ def __init__(self, parent_context=None):
+ self.parent = parent_context
+ self.bindings = {}
+
+ def become(self, region, value):
+ self.bindings[region] = value
+
+ def get_abstract_value(self, abstract_region):
+ return self.bindings[abstract_region]
+
+ def lookup_abstract_value(self, abstract_region):
+ while self is not None:
+ try:
+ return self.get_abstract_value(abstract_region)
+ except KeyError:
+ self = self.parent
+
+ def aggregate_concreteness(self, aggregate_region):
+ def visitor(region):
+ if region.is_abstract():
+ raise VisitException
+
+ try:
+ walk(aggregate_region, visitor)
+ except VisitException:
+ return False
+ return True
+
+
+class VisitException(Exception):
+ pass
+
+
+def walk(root, visitor):
+ seen = set([root])
+ working = set([root])
+
+ while working:
+ region = working.pop()
+ remaining = seen.difference(region.contents())
+ working.update(remaining)
+ seen.update(remaining)
+ visitor(region)
+
+
+context = RegionContext
+aggregate = AggregateRegion
+concrete = ConcreteRegion
+semiconcrete = SemiConcreteRegion
+abstract = AbstractRegion
Added: pypy/branch/effect-analysis/pypy/translator/backendopt/effect/test/__init__.py
==============================================================================
Added: pypy/branch/effect-analysis/pypy/translator/backendopt/effect/test/test_model.py
==============================================================================
--- (empty file)
+++ pypy/branch/effect-analysis/pypy/translator/backendopt/effect/test/test_model.py Sat Oct 24 05:18:03 2009
@@ -0,0 +1,141 @@
+from pypy.translator.backendopt.effect.model import distinct, context, \
+ abstract, semiconcrete, concrete, flatten, aggregate
+
+class TestCanRaise(object):
+
+ def test_semiconcrete_distinct(self):
+ "unique semiconcrete regions are distinct"
+ self.semiconcrete_distinct(semiconcrete(), semiconcrete())
+
+ def semiconcrete_distinct(self, x, y):
+ assert distinct(x, y)
+ assert not distinct(x, x)
+
+ def test_semiconcrete_distinct_contexts(self):
+ "unique semiconcrete regions are distinct in their context"
+ self.semiconcrete_distinct_contexts(semiconcrete(), semiconcrete())
+
+ def semiconcrete_distinct_contexts(self, x, y):
+ assert distinct(x, y)
+ assert not distinct(x, x)
+
+ context0 = context()
+ context1 = context()
+ assert distinct(x.inside(context0), x.inside(context1))
+ assert distinct(x.inside(context0), y.inside(context0))
+ assert not distinct(x.inside(context0), x.inside(context0))
+
+ def test_abstract_nondistinct(self):
+ "unless they have different type, abstract regions are not distinct"
+ x = abstract()
+ y = abstract()
+
+ assert not distinct(x, y)
+ assert not distinct(x, x)
+
+ def test_abstract_become(self):
+ "abstract regions may become concrete or semiconcrete"
+ x = abstract()
+ y = abstract()
+
+ specialisation_context = context()
+
+ specialisation_context.become(x, semiconcrete())
+
+ assert not distinct(x.inside(specialisation_context), y)
+
+ specialisation_context.become(x, semiconcrete())
+
+ assert distinct(x.inside(specialisation_context),
+ y.inside(specialisation_context))
+
+ specialisation_context = context()
+ some_concrete = semiconcrete()
+
+ specialisation_context.become(x, some_concrete)
+ assert not distinct(x.inside(specialisation_context), y)
+
+ specialisation_context.become(y, some_concrete)
+ assert not distinct(x.inside(specialisation_context),
+ y.inside(specialisation_context))
+
+ def test_abstract_semiconcrete(self):
+ """The distinctness of semiconcrete and abstract may be
+ glorked from context.
+
+ That is to say, the regions that are abstract and concrete in a
+ given context are distinct. This is because a region cannot both
+ come from inside and outside the function call.
+
+ Actually, this is not true, the concrete region could have
+ been passed to some other function, and the abstract region is
+ the retun value of the function.
+
+ shrug.
+ """
+
+ x = abstract()
+ specialisation_context = context()
+ y = semiconcrete(context=specialisation_context)
+
+ assert distinct(x, y)
+
+ assert not distinct(x.inside(specialisation_context),
+ y.inside(specialisation_context))
+
+
+ def test_index(self):
+ r1 = concrete()
+ r2 = concrete()
+
+ w, x, y, z = [concrete() for _ in xrange(4)]
+ r1.set_item(w)
+ r1.set_item(x)
+ r2.set_item(y)
+ r2.set_item(z)
+
+ assert distinct(r1.get_item(), r2.get_item())
+
+ r1.set_item(y)
+
+ assert not distinct(r1.get_item(), r2.get_item())
+
+ def test_attr(self):
+ r1 = concrete()
+ r2 = concrete()
+
+ w, x, y, z = [concrete() for _ in xrange(4)]
+ r1.set_item(w, 'w')
+ r1.set_item(x, 'w')
+ r2.set_item(w, 'w')
+ r2.set_item(x, 'x')
+
+ assert distinct(r2.get_item('x'), r2.get_item('w'))
+ assert distinct(r1.get_item('x'), r2.get_item('x'))
+ assert not distinct(r1.get_item('w'), r2.get_item('w'))
+
+ def test_aggregates(self):
+ x = aggregate()
+ y = aggregate([x])
+ z = aggregate([y])
+ x.add(z)
+
+ flatten([x])
+
+ assert distinct(x, abstract())
+
+ c = concrete()
+ x = aggregate([c])
+ y = aggregate([x])
+ z = aggregate([y])
+ x.add(z)
+
+ flatten([x])
+
+ assert not distinct(x, c)
+ assert x.regions == y.regions == z.regions
+
+ c = concrete()
+ y.add(c)
+
+ assert len(x.regions) == 2
More information about the Pypy-commit
mailing list