aboutsummaryrefslogtreecommitdiff
path: root/pypy
diff options
context:
space:
mode:
authorCarl Friedrich Bolz-Tereick <cfbolz@gmx.de>2020-12-31 14:20:17 +0100
committerCarl Friedrich Bolz-Tereick <cfbolz@gmx.de>2020-12-31 14:20:17 +0100
commit345be987d7042b9193047957055db77df78767a9 (patch)
tree6bf5cf4e0153bd5b98f0c98e8d23de57c7aefcf8 /pypy
parentfix remaining tests (diff)
downloadpypy-345be987d7042b9193047957055db77df78767a9.tar.gz
pypy-345be987d7042b9193047957055db77df78767a9.tar.bz2
pypy-345be987d7042b9193047957055db77df78767a9.zip
implement type freezing for unboxed types:
if the types of a field aren't stable, just give up on unboxing any field for the type
Diffstat (limited to 'pypy')
-rw-r--r--pypy/objspace/std/mapdict.py40
-rw-r--r--pypy/objspace/std/test/test_mapdict.py37
2 files changed, 69 insertions, 8 deletions
diff --git a/pypy/objspace/std/mapdict.py b/pypy/objspace/std/mapdict.py
index 1b03f6d983..74c153145c 100644
--- a/pypy/objspace/std/mapdict.py
+++ b/pypy/objspace/std/mapdict.py
@@ -234,7 +234,7 @@ class AbstractAttribute(object):
current = self
for i in range(number_to_readd):
assert isinstance(current, PlainAttribute)
- w_self_value = current._direct_read(obj)
+ w_self_value = current._prim_direct_read(obj)
stack[stack_index] = erase_map(current)
stack[stack_index + 1] = erase_item(w_self_value)
stack_index += 2
@@ -386,12 +386,14 @@ class PlainAttribute(AbstractAttribute):
self.order = len(back.cache_attrs) if back.cache_attrs else 0
def _copy_attr(self, obj, new_obj):
- w_value = self.read(obj, self.name, self.attrkind)
+ w_value = self._prim_direct_read(obj)
new_obj._get_mapdict_map().add_attr(new_obj, self.name, self.attrkind, w_value)
def _direct_read(self, obj):
return unerase_item(obj._mapdict_read_storage(self.storageindex))
+ _prim_direct_read = _direct_read
+
@jit.elidable
def _pure_direct_read(self, obj):
return self._direct_read(obj)
@@ -442,7 +444,7 @@ class PlainAttribute(AbstractAttribute):
new_obj = self.back.materialize_r_dict(space, obj, dict_w)
if self.attrkind == DICT:
w_attr = space.newtext(self.name)
- dict_w[w_attr] = self._direct_read(obj)
+ dict_w[w_attr] = self._prim_direct_read(obj)
else:
self._copy_attr(obj, new_obj)
return new_obj
@@ -450,7 +452,7 @@ class PlainAttribute(AbstractAttribute):
def materialize_str_dict(self, space, obj, str_dict):
new_obj = self.back.materialize_str_dict(space, obj, str_dict)
if self.attrkind == DICT:
- str_dict[self.name] = self._direct_read(obj)
+ str_dict[self.name] = self._prim_direct_read(obj)
else:
self._copy_attr(obj, new_obj)
return new_obj
@@ -506,7 +508,21 @@ class UnboxedPlainAttribute(PlainAttribute):
else:
return space.newfloat(val)
+ def _convert_to_boxed(self, obj):
+ new_obj = obj._get_mapdict_map().copy(obj)
+ map = new_obj.map
+ obj._set_mapdict_storage_and_map(new_obj.storage, map)
+ return map
+
def _direct_read(self, obj):
+ w_res = self._prim_direct_read(obj)
+ if self.terminator.allow_unboxing == False:
+ # oops, some other object using the same class isn't type stable!
+ # stop using boxing altogether to not get too many variants of maps
+ self._convert_to_boxed(obj)
+ return w_res
+
+ def _prim_direct_read(self, obj):
return self._box(unerase_unboxed(obj._mapdict_read_storage(self.storageindex))[self.listindex])
def _pure_direct_read(self, obj):
@@ -514,9 +530,19 @@ class UnboxedPlainAttribute(PlainAttribute):
return self._direct_read(obj)
def _direct_write(self, obj, w_value):
- val = self._unbox(w_value)
- unboxed = erase_unboxed(obj._mapdict_read_storage(self.storageindex))
- unboxed[self.listindex] = val
+ if type(w_value) is self.typ:
+ val = self._unbox(w_value)
+ unboxed = erase_unboxed(obj._mapdict_read_storage(self.storageindex))
+ unboxed[self.listindex] = val
+ return
+ # type change not supposed to happen. according to the principle
+ # of type freezing, we just give up, and will never unbox anything
+ # from that class again
+ self.terminator.allow_unboxing = False
+ map = self._convert_to_boxed(obj)
+ # now obj won't have any UnboxedPlainAttribute in its chain any
+ # more, because allow_unboxing is False
+ map.write(obj, self.name, self.attrkind, w_value)
def _switch_map_and_write_storage(self, obj, w_value):
from rpython.rlib.debug import make_sure_not_resized
diff --git a/pypy/objspace/std/test/test_mapdict.py b/pypy/objspace/std/test/test_mapdict.py
index 41c54fbbbd..45ed1615c1 100644
--- a/pypy/objspace/std/test/test_mapdict.py
+++ b/pypy/objspace/std/test/test_mapdict.py
@@ -549,7 +549,7 @@ def test_size_prediction():
assert c.terminator.size_estimate() in [(i + 10) // 2, (i + 11) // 2]
# ___________________________________________________________
-# unwrapped tests
+# unboxed tests
def test_unboxed_compute_indices():
w_cls = "class"
@@ -606,6 +606,41 @@ def test_unboxed_write_mixed():
w_obj.getdictvalue(space, "c") == 20.1
w_obj.setdictvalue(space, "d", None)
+def test_unboxed_type_change():
+ cls = Class(allow_unboxing=True)
+ w_obj = cls.instantiate(space)
+ w_obj.setdictvalue(space, "b", 15)
+ w_obj.setdictvalue(space, "b", 15.5)
+ assert w_obj.getdictvalue(space, "b") == 15.5
+ assert type(w_obj.map) is PlainAttribute
+ assert w_obj.map.terminator.allow_unboxing == False
+
+ w_obj = cls.instantiate(space)
+ w_obj.setdictvalue(space, "b", 15)
+ # next time we won't unbox
+ assert type(w_obj.map) is PlainAttribute
+
+def test_unboxed_type_change_other_object():
+ cls = Class(allow_unboxing=True)
+ w_obj1 = cls.instantiate(space)
+ w_obj1.setdictvalue(space, "b", 15)
+ w_obj2 = cls.instantiate(space)
+ w_obj2.setdictvalue(space, "b", 16)
+ assert w_obj1.map is w_obj2.map
+ assert type(w_obj1.map) is UnboxedPlainAttribute
+
+ # type change
+ w_obj1.setdictvalue(space, "b", 15.5)
+ assert w_obj1.getdictvalue(space, "b") == 15.5
+ assert type(w_obj1.map) is PlainAttribute
+ assert w_obj1.map.terminator.allow_unboxing == False
+
+ # w_obj2 is unaffected so far
+ assert type(w_obj2.map) is UnboxedPlainAttribute
+ assert w_obj2.getdictvalue(space, "b") == 16
+ # now it's switched
+ assert type(w_obj2.map) is PlainAttribute
+
# ___________________________________________________________
# dict tests