[pypy-svn] r26822 - in pypy/dist/pypy: annotation rpython rpython/lltypesystem rpython/ootypesystem rpython/test
arigo at codespeak.net
arigo at codespeak.net
Fri May 5 19:29:44 CEST 2006
Author: arigo
Date: Fri May 5 19:29:42 2006
New Revision: 26822
Modified:
pypy/dist/pypy/annotation/bookkeeper.py
pypy/dist/pypy/annotation/description.py
pypy/dist/pypy/rpython/lltypesystem/rclass.py
pypy/dist/pypy/rpython/lltypesystem/rpbc.py
pypy/dist/pypy/rpython/normalizecalls.py
pypy/dist/pypy/rpython/ootypesystem/rclass.py
pypy/dist/pypy/rpython/rpbc.py
pypy/dist/pypy/rpython/test/test_rpbc.py
Log:
Half refactoring of the AttrFamily: it is unchanged for FrozenDescs,
but for ClassDescs it now records attribute access for each attribute
name independently. See new test in test_rpbc.
This is prompted by PyPy translation crashes: introducing a memo
function on the pbc of all W_Root subclasses forced them to be all in
the same AttrFamily, which had terribly bad effects, as follows: a few
subclasses of ast.Node are instantiated generically (by calling a
variable that can be any of them). The same is true for the subclasses
of PyFrame. However, the __init__ in these two parts of the hierarchy
are completely different. So any common pbc attribute on all W_Root
subclasses caused the __init__ to come together too => obsure crash.
Modified: pypy/dist/pypy/annotation/bookkeeper.py
==============================================================================
--- pypy/dist/pypy/annotation/bookkeeper.py (original)
+++ pypy/dist/pypy/annotation/bookkeeper.py Fri May 5 19:29:42 2006
@@ -165,7 +165,8 @@
self.dictdefs = {} # map position_keys to DictDefs
self.immutable_cache = {}
- self.pbc_maximal_access_sets = UnionFind(description.AttrFamily)
+ self.classpbc_attr_families = {} # {'attr': UnionFind(ClassAttrFamily)}
+ self.frozenpbc_attr_families = UnionFind(description.FrozenAttrFamily)
self.pbc_maximal_call_families = UnionFind(description.CallFamily)
self.emulated_pbc_calls = {}
@@ -533,6 +534,17 @@
o.knowntype = t
return o
+ def get_classpbc_attr_families(self, attrname):
+ """Return the UnionFind for the ClassAttrFamilies corresponding to
+ attributes of the given name.
+ """
+ map = self.classpbc_attr_families
+ try:
+ access_sets = map[attrname]
+ except KeyError:
+ access_sets = map[attrname] = UnionFind(description.ClassAttrFamily)
+ return access_sets
+
def pbc_getattr(self, pbc, s_attr):
assert s_attr.is_constant()
attr = s_attr.const
@@ -541,8 +553,8 @@
if not descs:
return SomeImpossibleValue()
first = descs[0]
- change = first.mergeattrfamilies(*descs[1:])
- attrfamily = first.getattrfamily()
+ change = first.mergeattrfamilies(descs[1:], attr)
+ attrfamily = first.getattrfamily(attr)
position = self.position_key
attrfamily.read_locations[position] = True
@@ -552,8 +564,8 @@
actuals.append(desc.s_read_attribute(attr))
s_result = unionof(*actuals)
- attrfamily.attrs[attr] = unionof(s_result,
- attrfamily.attrs.get(attr, s_ImpossibleValue))
+ s_oldvalue = attrfamily.get_s_value(attr)
+ attrfamily.set_s_value(attr, unionof(s_result, s_oldvalue))
if change:
for position in attrfamily.read_locations:
Modified: pypy/dist/pypy/annotation/description.py
==============================================================================
--- pypy/dist/pypy/annotation/description.py (original)
+++ pypy/dist/pypy/annotation/description.py Fri May 5 19:29:42 2006
@@ -47,15 +47,15 @@
self.total_calltable_size += 1
-class AttrFamily:
- """A family of Desc objects that have common 'getattr' sites.
- The attr families are conceptually a partition of FrozenDesc and ClassDesc
- objects, where the equivalence relation is the transitive closure of
- 'd1~d2 if d1 and d2 might have an attribute read on them by the same
- getattr operation.'
+class FrozenAttrFamily:
+ """A family of FrozenDesc objects that have any common 'getattr' sites.
+ The attr families are conceptually a partition of FrozenDesc objects,
+ where the equivalence relation is the transitive closure of:
+ d1~d2 if d1 and d2 might have some attribute read on them by the same
+ getattr operation.
"""
def __init__(self, desc):
- self.descs = { desc: True }
+ self.descs = {desc: True}
self.read_locations = {} # set of position_keys
self.attrs = {} # { attr: s_value }
@@ -64,6 +64,49 @@
self.read_locations.update(other.read_locations)
self.attrs.update(other.attrs)
+ def get_s_value(self, attrname):
+ try:
+ return self.attrs[attrname]
+ except KeyError:
+ from pypy.annotation.model import s_ImpossibleValue
+ return s_ImpossibleValue
+
+ def set_s_value(self, attrname, s_value):
+ self.attrs[attrname] = s_value
+
+
+class ClassAttrFamily:
+ """A family of ClassDesc objects that have common 'getattr' sites for a
+ given attribute name. The attr families are conceptually a partition
+ of ClassDesc objects, where the equivalence relation is the transitive
+ closure of: d1~d2 if d1 and d2 might have a common attribute 'attrname'
+ read on them by the same getattr operation.
+
+ The 'attrname' is not explicitly stored here, but is the key used
+ in the dictionary bookkeeper.pbc_maximal_access_sets_map.
+ """
+ # The difference between ClassAttrFamily and FrozenAttrFamily is that
+ # FrozenAttrFamily is the union for all attribute names, but
+ # ClassAttrFamily is more precise: it is only about one attribut name.
+
+ def __init__(self, desc):
+ from pypy.annotation.model import s_ImpossibleValue
+ self.descs = { desc: True }
+ self.read_locations = {} # set of position_keys
+ self.s_value = s_ImpossibleValue # union of possible values
+
+ def update(self, other):
+ from pypy.annotation.model import unionof
+ self.descs.update(other.descs)
+ self.read_locations.update(other.read_locations)
+ self.s_value = unionof(self.s_value, other.s_value)
+
+ def get_s_value(self, attrname):
+ return self.s_value
+
+ def set_s_value(self, attrname, s_value):
+ self.s_value = s_value
+
# ____________________________________________________________
class Desc(object):
@@ -102,30 +145,6 @@
changed = changed or changed1
return changed
- def getattrfamily(self):
- """Get the AttrFamily object. Possibly creates one."""
- access_sets = self.bookkeeper.pbc_maximal_access_sets
- _, _, attrfamily = access_sets.find(self)
- return attrfamily
-
- def queryattrfamily(self):
- """Retrieve the AttrFamily object if there is one, otherwise
- return None."""
- access_sets = self.bookkeeper.pbc_maximal_access_sets
- try:
- return access_sets[self]
- except KeyError:
- return None
-
- def mergeattrfamilies(self, *others):
- """Merge the attr families of the given Descs into one."""
- access_sets = self.bookkeeper.pbc_maximal_access_sets
- changed, rep, attrfamily = access_sets.find(self)
- for desc in others:
- changed1, rep, attrfamily = access_sets.union(rep, desc)
- changed = changed or changed1
- return changed
-
def bind_under(self, classdef, name):
return self
@@ -548,6 +567,30 @@
def rowkey(self):
return self
+ def getattrfamily(self, attrname):
+ "Get the ClassAttrFamily object for attrname. Possibly creates one."
+ access_sets = self.bookkeeper.get_classpbc_attr_families(attrname)
+ _, _, attrfamily = access_sets.find(self)
+ return attrfamily
+
+ def queryattrfamily(self, attrname):
+ """Retrieve the ClassAttrFamily object for attrname if there is one,
+ otherwise return None."""
+ access_sets = self.bookkeeper.get_classpbc_attr_families(attrname)
+ try:
+ return access_sets[self]
+ except KeyError:
+ return None
+
+ def mergeattrfamilies(self, others, attrname):
+ """Merge the attr families of the given Descs into one."""
+ access_sets = self.bookkeeper.get_classpbc_attr_families(attrname)
+ changed, rep, attrfamily = access_sets.find(self)
+ for desc in others:
+ changed1, rep, attrfamily = access_sets.union(rep, desc)
+ changed = changed or changed1
+ return changed
+
class MethodDesc(Desc):
knowntype = types.MethodType
@@ -664,6 +707,30 @@
raise AssertionError("name clash: %r" % (name,))
self.attrcache[name] = value
+ def getattrfamily(self, attrname=None):
+ "Get the FrozenAttrFamily object for attrname. Possibly creates one."
+ access_sets = self.bookkeeper.frozenpbc_attr_families
+ _, _, attrfamily = access_sets.find(self)
+ return attrfamily
+
+ def queryattrfamily(self, attrname=None):
+ """Retrieve the FrozenAttrFamily object for attrname if there is one,
+ otherwise return None."""
+ access_sets = self.bookkeeper.frozenpbc_attr_families
+ try:
+ return access_sets[self]
+ except KeyError:
+ return None
+
+ def mergeattrfamilies(self, others, attrname=None):
+ """Merge the attr families of the given Descs into one."""
+ access_sets = self.bookkeeper.frozenpbc_attr_families
+ changed, rep, attrfamily = access_sets.find(self)
+ for desc in others:
+ changed1, rep, attrfamily = access_sets.union(rep, desc)
+ changed = changed or changed1
+ return changed
+
class MethodOfFrozenDesc(Desc):
knowntype = types.MethodType
Modified: pypy/dist/pypy/rpython/lltypesystem/rclass.py
==============================================================================
--- pypy/dist/pypy/rpython/lltypesystem/rclass.py (original)
+++ pypy/dist/pypy/rpython/lltypesystem/rclass.py Fri May 5 19:29:42 2006
@@ -114,12 +114,11 @@
# attributes showing up in getattrs done on the class as a PBC
extra_access_sets = self.rtyper.class_pbc_attributes.get(
self.classdef, {})
- for access_set, counter in extra_access_sets.items():
- for attr, s_value in access_set.attrs.items():
- r = self.rtyper.getrepr(s_value)
- mangled_name = mangle('pbc%d' % counter, attr)
- pbcfields[access_set, attr] = mangled_name, r
- llfields.append((mangled_name, r.lowleveltype))
+ for access_set, (attr, counter) in extra_access_sets.items():
+ r = self.rtyper.getrepr(access_set.s_value)
+ mangled_name = mangle('pbc%d' % counter, attr)
+ pbcfields[access_set, attr] = mangled_name, r
+ llfields.append((mangled_name, r.lowleveltype))
#
self.rbase = getclassrepr(self.rtyper, self.classdef.basedef)
self.rbase.setup()
Modified: pypy/dist/pypy/rpython/lltypesystem/rpbc.py
==============================================================================
--- pypy/dist/pypy/rpython/lltypesystem/rpbc.py (original)
+++ pypy/dist/pypy/rpython/lltypesystem/rpbc.py Fri May 5 19:29:42 2006
@@ -177,14 +177,14 @@
# instantiating a class from multiple possible classes
from pypy.rpython.lltypesystem.rbuiltin import ll_instantiate
vtypeptr = hop.inputarg(self, arg=0)
- access_set = self.get_access_set()
- r_class = self.get_class_repr()
- if '__init__' in access_set.attrs:
- s_init = access_set.attrs['__init__']
+ try:
+ access_set, r_class = self.get_access_set('__init__')
+ except MissingRTypeAttribute:
+ s_init = annmodel.s_ImpossibleValue
+ else:
+ s_init = access_set.s_value
v_init = r_class.getpbcfield(vtypeptr, access_set, '__init__',
hop.llops)
- else:
- s_init = annmodel.s_ImpossibleValue
v_inst1 = hop.gendirectcall(ll_instantiate, vtypeptr)
v_instance = hop.genop('cast_pointer', [v_inst1],
resulttype = r_instance)
Modified: pypy/dist/pypy/rpython/normalizecalls.py
==============================================================================
--- pypy/dist/pypy/rpython/normalizecalls.py (original)
+++ pypy/dist/pypy/rpython/normalizecalls.py Fri May 5 19:29:42 2006
@@ -186,29 +186,30 @@
# PBC access set of the family of classes of 'some_class'. If the classes
# have corresponding ClassDefs, they are not updated by the annotator.
# We have to do it now.
- access_sets = rtyper.annotator.bookkeeper.pbc_maximal_access_sets
- for access_set in access_sets.infos():
- descs = access_set.descs
- if len(descs) <= 1:
- continue
- if not isinstance(descs.iterkeys().next(), description.ClassDesc):
- continue
- classdefs = [desc.getuniqueclassdef() for desc in descs]
- commonbase = classdefs[0]
- for cdef in classdefs[1:]:
- commonbase = commonbase.commonbase(cdef)
- if commonbase is None:
- raise TyperError("reading attributes %r: no common base class "
- "for %r" % (
- access_set.attrs.keys(), descs.keys()))
- extra_access_sets = rtyper.class_pbc_attributes.setdefault(commonbase,
- {})
- if commonbase in rtyper.class_reprs:
- assert access_set in extra_access_sets # minimal sanity check
- return
- access_set.commonbase = commonbase
- if access_set not in extra_access_sets:
- extra_access_sets[access_set] = len(extra_access_sets)
+ all_families = rtyper.annotator.bookkeeper.classpbc_attr_families
+ for attrname, access_sets in all_families.items():
+ for access_set in access_sets.infos():
+ descs = access_set.descs
+ if len(descs) <= 1:
+ continue
+ if not isinstance(descs.iterkeys().next(), description.ClassDesc):
+ continue
+ classdefs = [desc.getuniqueclassdef() for desc in descs]
+ commonbase = classdefs[0]
+ for cdef in classdefs[1:]:
+ commonbase = commonbase.commonbase(cdef)
+ if commonbase is None:
+ raise TyperError("reading attribute %r: no common base "
+ "class for %r" % (attrname, descs.keys()))
+ extra_access_sets = rtyper.class_pbc_attributes.setdefault(
+ commonbase, {})
+ if commonbase in rtyper.class_reprs:
+ assert access_set in extra_access_sets # minimal sanity check
+ continue
+ access_set.commonbase = commonbase
+ if access_set not in extra_access_sets:
+ counter = len(extra_access_sets)
+ extra_access_sets[access_set] = attrname, counter
# ____________________________________________________________
@@ -222,19 +223,17 @@
descs = family.descs.keys()
if not isinstance(descs[0], description.ClassDesc):
continue
- # Note that a callfamily of classes must really be in the same
- # attrfamily as well; This property is relied upon on various
- # places in the rtyper
- change = descs[0].mergeattrfamilies(*descs[1:])
+ # Note that if classes are in the same callfamily, their __init__
+ # attribute must be in the same attrfamily as well.
+ change = descs[0].mergeattrfamilies(descs[1:], '__init__')
if hasattr(descs[0].getuniqueclassdef(), 'my_instantiate_graph'):
assert not change, "after the fact change to a family of classes" # minimal sanity check
return
- attrfamily = descs[0].getattrfamily()
# Put __init__ into the attr family, for ClassesPBCRepr.call()
- s_value = attrfamily.attrs.get('__init__', annmodel.s_ImpossibleValue)
+ attrfamily = descs[0].getattrfamily('__init__')
inits_s = [desc.s_read_attribute('__init__') for desc in descs]
- s_value = annmodel.unionof(s_value, *inits_s)
- attrfamily.attrs['__init__'] = s_value
+ s_value = annmodel.unionof(attrfamily.s_value, *inits_s)
+ attrfamily.s_value = s_value
# ClassesPBCRepr.call() will also need instantiate() support
for desc in descs:
bk.needs_generic_instantiate[desc.getuniqueclassdef()] = True
Modified: pypy/dist/pypy/rpython/ootypesystem/rclass.py
==============================================================================
--- pypy/dist/pypy/rpython/ootypesystem/rclass.py (original)
+++ pypy/dist/pypy/rpython/ootypesystem/rclass.py Fri May 5 19:29:42 2006
@@ -53,12 +53,11 @@
# attributes showing up in getattrs done on the class as a PBC
extra_access_sets = self.rtyper.class_pbc_attributes.get(
self.classdef, {})
- for access_set, counter in extra_access_sets.items():
- for attr, s_value in access_set.attrs.items():
- r = self.rtyper.getrepr(s_value)
- mangled_name = pbcmangle('pbc%d' % counter, attr)
- pbcfields[access_set, attr] = mangled_name, r
- llfields.append((mangled_name, r.lowleveltype))
+ for access_set, (attr, counter) in extra_access_sets.items():
+ r = self.rtyper.getrepr(access_set.s_value)
+ mangled_name = pbcmangle('pbc%d' % counter, attr)
+ pbcfields[access_set, attr] = mangled_name, r
+ llfields.append((mangled_name, r.lowleveltype))
self.rbase.setup()
ootype.addFields(self.lowleveltype, dict(llfields))
Modified: pypy/dist/pypy/rpython/rpbc.py
==============================================================================
--- pypy/dist/pypy/rpython/rpbc.py (original)
+++ pypy/dist/pypy/rpython/rpbc.py Fri May 5 19:29:42 2006
@@ -599,24 +599,22 @@
self.lowleveltype = Void
else:
self.lowleveltype = rtyper.type_system.rclass.CLASSTYPE
- self._access_set = None
- self._class_repr = None
- def get_access_set(self):
- if self._access_set is None:
- classdescs = self.s_pbc.descriptions.keys()
- access = classdescs[0].getattrfamily()
- for classdesc in classdescs[1:]:
- access1 = classdesc.getattrfamily()
- assert access1 is access # XXX not implemented
- commonbase = access.commonbase
- self._class_repr = rclass.getclassrepr(self.rtyper, commonbase)
- self._access_set = access
- return self._access_set
-
- def get_class_repr(self):
- self.get_access_set()
- return self._class_repr
+ def get_access_set(self, attrname):
+ """Return the ClassAttrFamily corresponding to accesses to 'attrname'
+ and the ClassRepr of the class which stores this attribute in
+ its vtable.
+ """
+ classdescs = self.s_pbc.descriptions.keys()
+ access = classdescs[0].queryattrfamily(attrname)
+ for classdesc in classdescs[1:]:
+ access1 = classdesc.queryattrfamily(attrname)
+ assert access1 is access # XXX not implemented
+ if access is None:
+ raise rclass.MissingRTypeAttribute(attrname)
+ commonbase = access.commonbase
+ class_repr = rclass.getclassrepr(self.rtyper, commonbase)
+ return access, class_repr
def convert_desc(self, desc):
if desc not in self.s_pbc.descriptions:
@@ -635,11 +633,10 @@
return hop.inputconst(hop.r_result, hop.s_result.const)
else:
attr = hop.args_s[1].const
- access_set = self.get_access_set()
- class_repr = self.get_class_repr()
+ access_set, class_repr = self.get_access_set(attr)
vcls, vattr = hop.inputargs(class_repr, Void)
v_res = class_repr.getpbcfield(vcls, access_set, attr, hop.llops)
- s_res = access_set.attrs[attr]
+ s_res = access_set.s_value
r_res = self.rtyper.getrepr(s_res)
return hop.llops.convertvar(v_res, r_res, hop.r_result)
@@ -663,11 +660,8 @@
if r_clspbc.lowleveltype is Void:
return inputconst(r_cls, r_clspbc.s_pbc.const)
# convert from ptr-to-object-vtable to ptr-to-more-precise-vtable
- # but first check if it is safe
assert (r_clspbc.lowleveltype ==
r_clspbc.rtyper.type_system.rclass.CLASSTYPE)
- if not r_clspbc.get_class_repr().classdef.issubclass(r_cls.classdef):
- return NotImplemented
return r_cls.fromclasstype(v, llops)
class __extend__(pairtype(AbstractClassesPBCRepr, AbstractClassesPBCRepr)):
Modified: pypy/dist/pypy/rpython/test/test_rpbc.py
==============================================================================
--- pypy/dist/pypy/rpython/test/test_rpbc.py (original)
+++ pypy/dist/pypy/rpython/test/test_rpbc.py Fri May 5 19:29:42 2006
@@ -1220,6 +1220,33 @@
res = interpret(f, [1], type_system=self.ts)
assert res == 65
+ def test_multiple_attribute_access_patterns(self):
+ class Base(object):
+ pass
+ class A(Base):
+ value = 1000
+ def meth(self): return self.n + 1
+ class B(A):
+ def meth(self): return self.n + 2
+ class C(Base):
+ value = 2000
+ def meth(self): ShouldNotBeSeen
+ def AorB(n):
+ if n == 5: return A
+ else: return B
+ def BorC(n):
+ if n == 3: return B
+ else: return C
+ def f(n):
+ value = BorC(n).value
+ x = B()
+ x.n = 100
+ return value + AorB(n).meth(x)
+
+ for i in [1, 3, 5]:
+ res = interpret(f, [i])
+ assert res == f(i)
+
def test_call_from_list():
# Don't test with ootype, since it doesn't support lists in a
More information about the Pypy-commit
mailing list