Author: Carl Friedrich Bolz <[email protected]>
Branch: value-profiling
Changeset: r81861:bde790a86620
Date: 2016-01-15 20:56 +0100
http://bitbucket.org/pypy/pypy/changeset/bde790a86620/
Log: merge default
(superficial merge, will need fixes to integrate the two approaches)
diff --git a/pypy/module/pypyjit/test_pypy_c/test_instance.py
b/pypy/module/pypyjit/test_pypy_c/test_instance.py
--- a/pypy/module/pypyjit/test_pypy_c/test_instance.py
+++ b/pypy/module/pypyjit/test_pypy_c/test_instance.py
@@ -30,7 +30,7 @@
jump(..., descr=...)
""")
- def test_load_attr(self):
+ def test_load_immutable_attr(self):
src = '''
class A(object):
pass
diff --git a/pypy/module/pypyjit/test_pypy_c/test_misc.py
b/pypy/module/pypyjit/test_pypy_c/test_misc.py
--- a/pypy/module/pypyjit/test_pypy_c/test_misc.py
+++ b/pypy/module/pypyjit/test_pypy_c/test_misc.py
@@ -114,13 +114,16 @@
assert log.result == main(1000)
loop, = log.loops_by_filename(self.filepath)
assert loop.match("""
- guard_not_invalidated(descr=...)
i12 = int_is_true(i4)
guard_true(i12, descr=...)
- i14 = int_add_ovf(i13, 2)
+ guard_not_invalidated(descr=...)
+ i13 = int_add_ovf(i8, i9)
guard_no_overflow(descr=...)
- i13 = int_add_ovf(i14, 2)
+ i10 = int_mul_ovf(2, i61)
guard_no_overflow(descr=...)
+ i14 = int_add_ovf(i13, i10)
+ guard_no_overflow(descr=...)
+ setfield_gc(p7, i11, descr=...)
i17 = int_sub_ovf(i4, 1)
guard_no_overflow(descr=...)
--TICK--
diff --git a/pypy/module/pypyjit/test_pypy_c/test_thread.py
b/pypy/module/pypyjit/test_pypy_c/test_thread.py
--- a/pypy/module/pypyjit/test_pypy_c/test_thread.py
+++ b/pypy/module/pypyjit/test_pypy_c/test_thread.py
@@ -41,11 +41,13 @@
assert round(log.result, 6) == round(main(500), 6)
loop, = log.loops_by_filename(self.filepath)
assert loop.match("""
- guard_not_invalidated(descr=...)
i53 = int_lt(i48, i27)
guard_true(i53, descr=...)
- i54 = int_add(i48, 1)
+ guard_not_invalidated(descr=...)
+ i54 = int_add_ovf(i48, i47)
+ guard_no_overflow(descr=...)
--TICK--
+ i58 = arraylen_gc(p43, descr=...)
jump(..., descr=...)
""")
diff --git a/pypy/objspace/std/celldict.py b/pypy/objspace/std/celldict.py
--- a/pypy/objspace/std/celldict.py
+++ b/pypy/objspace/std/celldict.py
@@ -10,17 +10,13 @@
DictStrategy, ObjectDictStrategy, _never_equal_to_string,
create_iterator_classes)
from pypy.objspace.std.typeobject import (
- MutableCell, IntMutableCell, ObjectMutableCell, write_cell)
+ MutableCell, IntMutableCell, ObjectMutableCell, write_cell,
+ unwrap_cell)
class VersionTag(object):
pass
-def unwrap_cell(space, w_value):
- if isinstance(w_value, MutableCell):
- return w_value.unwrap_cell(space)
- return w_value
-
def _wrapkey(space, key):
return space.wrap(key)
diff --git a/pypy/objspace/std/dictproxyobject.py
b/pypy/objspace/std/dictproxyobject.py
--- a/pypy/objspace/std/dictproxyobject.py
+++ b/pypy/objspace/std/dictproxyobject.py
@@ -4,7 +4,7 @@
from pypy.interpreter.error import OperationError, oefmt
from pypy.objspace.std.dictmultiobject import (
DictStrategy, create_iterator_classes)
-from pypy.objspace.std.typeobject import unwrap_cell
+from pypy.objspace.std.typeobject import unwrap_cell_iftypeversion
class DictProxyStrategy(DictStrategy):
@@ -84,11 +84,12 @@
return space.newlist_bytes(self.unerase(w_dict.dstorage).dict_w.keys())
def values(self, w_dict):
- return [unwrap_cell(self.space, w_value) for w_value in
self.unerase(w_dict.dstorage).dict_w.itervalues()]
+ return [unwrap_cell_iftypeversion(self.space, w_value)
+ for w_value in
self.unerase(w_dict.dstorage).dict_w.itervalues()]
def items(self, w_dict):
space = self.space
- return [space.newtuple([space.wrap(key), unwrap_cell(self.space,
w_value)])
+ return [space.newtuple([space.wrap(key),
unwrap_cell_iftypeversion(self.space, w_value)])
for (key, w_value) in
self.unerase(w_dict.dstorage).dict_w.iteritems()]
def clear(self, w_dict):
@@ -109,6 +110,6 @@
def wrapkey(space, key):
return space.wrap(key)
def wrapvalue(space, value):
- return unwrap_cell(space, value)
+ return unwrap_cell_iftypeversion(space, value)
create_iterator_classes(DictProxyStrategy)
diff --git a/pypy/objspace/std/mapdict.py b/pypy/objspace/std/mapdict.py
--- a/pypy/objspace/std/mapdict.py
+++ b/pypy/objspace/std/mapdict.py
@@ -10,8 +10,9 @@
BaseValueIterator, BaseItemIterator, _never_equal_to_string,
W_DictObject,
)
-from pypy.objspace.std.typeobject import MutableCell
-
+from pypy.objspace.std.typeobject import (
+ MutableCell, IntMutableCell, FloatMutableCell, ObjectMutableCell,
+ unwrap_cell)
# ____________________________________________________________
# attribute shapes
@@ -45,20 +46,16 @@
if w_res is not None:
return w_res
if (
- jit.isconstant(attr.storageindex) and
+ jit.isconstant(attr) and
jit.isconstant(obj) and
not attr.ever_mutated
):
- return self._pure_mapdict_read_storage(obj, attr.storageindex)
+ return attr._pure_read(obj)
else:
- w_res = obj._mapdict_read_storage(attr.storageindex)
+ result = obj._mapdict_read_storage(attr.storageindex)
if jit.we_are_jitted() and attr.class_is_known():
- jit.record_exact_class(w_res, attr.read_constant_cls())
- return w_res
-
- @jit.elidable
- def _pure_mapdict_read_storage(self, obj, storageindex):
- return obj._mapdict_read_storage(storageindex)
+ jit.record_exact_class(result, attr.read_constant_cls())
+ return attr._read_cell(result)
def write(self, obj, selector, w_value):
attr = self.find_map_attr(selector)
@@ -70,7 +67,9 @@
# if this path is taken, the storage is already filled from the time we
# did the map transition. Therefore, if the value profiler says so, we
# can not do the write
- if not write_unnecessary:
+ cell = obj._mapdict_read_storage(attr.storageindex)
+ w_value = attr._write_cell(cell, w_value)
+ if write_unnecessary and w_value is not None:
obj._mapdict_write_storage(attr.storageindex, w_value)
return True
@@ -172,6 +171,7 @@
def add_attr(self, obj, selector, w_value):
# grumble, jit needs this
attr = self._get_new_attr(selector[0], selector[1])
+ w_value = attr._write_cell(None, w_value)
oldattr = obj._get_mapdict_map()
if not jit.we_are_jitted():
size_est = (oldattr._size_estimate + attr.size_estimate()
@@ -188,6 +188,8 @@
# the order is important here: first change the map, then the storage,
# for the benefit of the special subclasses
obj._set_mapdict_map(attr)
+ w_value = attr._write_cell(None, w_value)
+ assert w_value is not None
obj._mapdict_write_storage(attr.storageindex, w_value)
attr.see_write(w_value)
@@ -296,7 +298,8 @@
class PlainAttribute(AbstractAttribute):
- _immutable_fields_ = ['selector', 'storageindex', 'back', 'ever_mutated?']
+ _immutable_fields_ = ['selector', 'storageindex', 'back',
+ 'ever_mutated?', 'can_contain_mutable_cell?']
objectmodel.import_from_mixin(valueprof.ValueProf)
def __init__(self, selector, back):
@@ -307,6 +310,18 @@
self._size_estimate = self.length() * NUM_DIGITS_POW2
self.ever_mutated = False
self.init_valueprof('%s.%s' % (back.terminator.w_cls.name if
back.terminator.w_cls else '???', selector[0]))
+ # this flag means: at some point there was an instance that used a
+ # derivative of this map that had a MutableCell stored into the
+ # corresponding field.
+ # if the flag is False, we don't need to unbox the attribute.
+ self.can_contain_mutable_cell = False
+
+ @jit.elidable
+ def _pure_read(self, obj):
+ # this is safe even if the mapdict stores a mutable cell. the cell can
+ # only be changed is ever_mutated is set to True
+ result = obj._mapdict_read_storage(self.storageindex)
+ return self._read_cell(result)
# ____________________________________________________________
# methods for ValueProf mixin
@@ -320,6 +335,37 @@
return w_obj.intval
# ____________________________________________________________
+ def _read_cell(self, w_cell):
+ if not self.can_contain_mutable_cell:
+ return w_cell
+ return unwrap_cell(self.space, w_cell)
+
+ def _write_cell(self, w_cell, w_value):
+ from pypy.objspace.std.intobject import W_IntObject
+ from pypy.objspace.std.floatobject import W_FloatObject
+ assert not isinstance(w_cell, ObjectMutableCell)
+ if type(w_value) is W_IntObject:
+ if isinstance(w_cell, IntMutableCell):
+ w_cell.intvalue = w_value.intval
+ return None
+ check = self._ensure_can_contain_mutable_cell()
+ assert check
+ return IntMutableCell(w_value.intval)
+ if type(w_value) is W_FloatObject:
+ if isinstance(w_cell, FloatMutableCell):
+ w_cell.floatvalue = w_value.floatval
+ return None
+ check = self._ensure_can_contain_mutable_cell()
+ assert check
+ return FloatMutableCell(w_value.floatval)
+ return w_value
+
+ @jit.elidable
+ def _ensure_can_contain_mutable_cell(self):
+ if not self.can_contain_mutable_cell:
+ self.can_contain_mutable_cell = True
+ return True
+
def _copy_attr(self, obj, new_obj):
w_value = self.read(obj, self.selector)
new_obj._get_mapdict_map().add_attr(new_obj, self.selector, w_value)
@@ -357,7 +403,8 @@
new_obj = self.back.materialize_r_dict(space, obj, dict_w)
if self.selector[1] == DICT:
w_attr = space.wrap(self.selector[0])
- dict_w[w_attr] = obj._mapdict_read_storage(self.storageindex)
+ dict_w[w_attr] = unwrap_cell(
+ space, obj._mapdict_read_storage(self.storageindex))
else:
self._copy_attr(obj, new_obj)
return new_obj
@@ -898,7 +945,8 @@
map = w_obj._get_mapdict_map()
if entry.is_valid_for_map(map) and entry.w_method is None:
# everything matches, it's incredibly fast
- return w_obj._mapdict_read_storage(entry.storageindex)
+ return unwrap_cell(
+ map.space, w_obj._mapdict_read_storage(entry.storageindex))
return LOAD_ATTR_slowpath(pycode, w_obj, nameindex, map)
LOAD_ATTR_caching._always_inline_ = True
@@ -943,7 +991,8 @@
# Note that if map.terminator is a DevolvedDictTerminator,
# map.find_map_attr will always return None if
selector[1]==DICT.
_fill_cache(pycode, nameindex, map, version_tag,
attr.storageindex)
- return w_obj._mapdict_read_storage(attr.storageindex)
+ return unwrap_cell(
+ space,
w_obj._mapdict_read_storage(attr.storageindex))
if space.config.objspace.std.withmethodcachecounter:
INVALID_CACHE_ENTRY.failure_counter += 1
return space.getattr(w_obj, w_name)
diff --git a/pypy/objspace/std/test/test_mapdict.py
b/pypy/objspace/std/test/test_mapdict.py
--- a/pypy/objspace/std/test/test_mapdict.py
+++ b/pypy/objspace/std/test/test_mapdict.py
@@ -109,23 +109,27 @@
assert obj2.map is obj.map
def test_attr_immutability(monkeypatch):
+ from pypy.objspace.std.intobject import W_IntObject
cls = Class()
obj = cls.instantiate()
- obj.setdictvalue(space, "a", 10)
- obj.setdictvalue(space, "b", 20)
- obj.setdictvalue(space, "b", 30)
- assert obj.storage == [10, 30]
+ obj.setdictvalue(space, "a", W_IntObject(10))
+ obj.setdictvalue(space, "b", W_IntObject(20))
+ obj.setdictvalue(space, "b", W_IntObject(30))
+ mutcella, mutcellb = obj.storage
+ assert mutcella.intvalue == 10
+ assert mutcellb.intvalue == 30
assert obj.map.ever_mutated == True
assert obj.map.back.ever_mutated == False
indices = []
+ orig_pure_read = PlainAttribute._pure_read
- def _pure_mapdict_read_storage(obj, storageindex):
- assert storageindex == 0
- indices.append(storageindex)
- return obj._mapdict_read_storage(storageindex)
+ def _pure_read(self, obj):
+ assert self.storageindex == 0
+ indices.append(self.storageindex)
+ return orig_pure_read(self, obj)
- obj.map._pure_mapdict_read_storage = _pure_mapdict_read_storage
+ monkeypatch.setattr(PlainAttribute, "_pure_read", _pure_read)
monkeypatch.setattr(jit, "isconstant", lambda c: True)
assert obj.getdictvalue(space, "a") == 10
@@ -134,16 +138,20 @@
assert indices == [0, 0]
obj2 = cls.instantiate()
- obj2.setdictvalue(space, "a", 15)
- obj2.setdictvalue(space, "b", 25)
+ obj2.setdictvalue(space, "a", W_IntObject(15))
+ obj2.setdictvalue(space, "b", W_IntObject(25))
+ mutcella, mutcellb = obj2.storage
assert obj2.map is obj.map
assert obj2.map.ever_mutated == True
assert obj2.map.back.ever_mutated == False
# mutating obj2 changes the map
- obj2.setdictvalue(space, "a", 50)
+ obj2.setdictvalue(space, "a", W_IntObject(50))
assert obj2.map.back.ever_mutated == True
assert obj2.map is obj.map
+ assert obj2.storage[0] is mutcella
+ assert obj2.storage[1] is mutcellb
+
def test_attr_immutability_delete():
cls = Class()
@@ -155,6 +163,94 @@
assert obj.map.ever_mutated == True
assert obj.map is map1
+def test_immutable_with_mutcell():
+ # even an immutable attribute will be stored as a mutcell. The reason is
+ # that then the type of the attribute is more predictable (eg always
+ # IntMutableCell and sometimes IntMutableCell and sometimes W_IntObject)
+ from pypy.objspace.std.intobject import W_IntObject
+ cls = Class()
+ obj = cls.instantiate()
+ # make sure the attribute counts as mutable
+ obj.setdictvalue(space, "a", W_IntObject(4))
+ # not wrapped because of the FakeSpace :-(
+ assert obj.getdictvalue(space, "a") == 4
+ mutcell = obj._mapdict_read_storage(0)
+ assert mutcell.intvalue == 4
+
+
+def test_mutcell_not_immutable():
+ from pypy.objspace.std.intobject import W_IntObject
+ cls = Class()
+ obj = cls.instantiate()
+ # make sure the attribute counts as mutable
+ obj.setdictvalue(space, "a", W_IntObject(4))
+ obj.setdictvalue(space, "a", W_IntObject(5))
+ assert obj.map.ever_mutated
+
+ obj = cls.instantiate()
+ obj.setdictvalue(space, "a", W_IntObject(5))
+ # not wrapped because of the FakeSpace :-(
+ assert obj.getdictvalue(space, "a") == 5
+ mutcell = obj._mapdict_read_storage(0)
+ assert mutcell.intvalue == 5
+
+ obj.setdictvalue(space, "a", W_IntObject(6))
+ assert obj.getdictvalue(space, "a") == 6 # FakeSpace again
+ mutcell1 = obj._mapdict_read_storage(0)
+ assert mutcell1.intvalue == 6
+ assert mutcell is mutcell1
+
+ obj.setdictvalue(space, "a", W_IntObject(7))
+ assert obj.getdictvalue(space, "a") == 7 # FakeSpace again
+ mutcell2 = obj._mapdict_read_storage(0)
+ assert mutcell2.intvalue == 7
+ assert mutcell2 is mutcell1
+
+
+def test_mutcell_not_immutable_float():
+ from pypy.objspace.std.floatobject import W_FloatObject
+ cls = Class()
+ obj = cls.instantiate()
+ # make sure the attribute counts as mutable
+ obj.setdictvalue(space, "a", W_FloatObject(4.43))
+ obj.setdictvalue(space, "a", W_FloatObject(5.43))
+ assert obj.map.ever_mutated
+
+ obj = cls.instantiate()
+ obj.setdictvalue(space, "a", W_FloatObject(5.43))
+ assert obj.getdictvalue(space, "a") == 5.43
+ mutcell = obj._mapdict_read_storage(0)
+ assert mutcell.floatvalue == 5.43
+
+ obj.setdictvalue(space, "a", W_FloatObject(6.43))
+ assert obj.getdictvalue(space, "a") == 6.43
+ mutcell1 = obj._mapdict_read_storage(0)
+ assert mutcell1.floatvalue == 6.43
+ assert mutcell is mutcell1
+
+ obj.setdictvalue(space, "a", W_FloatObject(7.43))
+ assert obj.getdictvalue(space, "a") == 7.43
+ mutcell2 = obj._mapdict_read_storage(0)
+ assert mutcell2.floatvalue == 7.43
+ assert mutcell2 is mutcell1
+
+
+def test_mutcell_unwrap_only_if_needed():
+ from pypy.objspace.std.intobject import W_IntObject
+ cls = Class()
+ obj = cls.instantiate()
+ obj.setdictvalue(space, "a", "foo")
+ assert not obj._get_mapdict_map().can_contain_mutable_cell
+ obj.setdictvalue(space, "a", W_IntObject(6))
+ obj.setdictvalue(space, "a", W_IntObject(6))
+ assert obj._get_mapdict_map().can_contain_mutable_cell
+
+ obj._get_mapdict_map().can_contain_mutable_cell = False
+ mutcell = IntMutableCell(1)
+ obj._mapdict_write_storage(0, mutcell)
+ assert obj.getdictvalue(space, "a") is mutcell # not unwrapped
+
+
def test_delete():
for i, dattr in enumerate(["a", "b", "c"]):
c = Class()
diff --git a/pypy/objspace/std/test/test_versionedtype.py
b/pypy/objspace/std/test/test_versionedtype.py
--- a/pypy/objspace/std/test/test_versionedtype.py
+++ b/pypy/objspace/std/test/test_versionedtype.py
@@ -259,6 +259,43 @@
cell = w_A._getdictvalue_no_unwrapping(space, "x")
assert space.float_w(cell.w_value) == 2.2
+ def test_float_cells(self):
+ space = self.space
+ w_x = space.wrap("x")
+ w_A, w_B, w_C = self.get_three_classes()
+ atag = w_A.version_tag()
+ space.setattr(w_A, w_x, space.newfloat(1.1))
+ assert w_A.version_tag() is not atag
+ assert space.float_w(space.getattr(w_A, w_x)) == 1.1
+
+ atag = w_A.version_tag()
+ space.setattr(w_A, w_x, space.newfloat(2.1))
+ assert w_A.version_tag() is not atag
+ assert space.float_w(space.getattr(w_A, w_x)) == 2.1
+ cell = w_A._getdictvalue_no_unwrapping(space, "x")
+ assert cell.floatvalue == 2.1
+
+ atag = w_A.version_tag()
+ space.setattr(w_A, w_x, space.newfloat(3.1))
+ assert w_A.version_tag() is atag
+ assert space.float_w(space.getattr(w_A, w_x)) == 3.1
+ assert cell.floatvalue == 3.1
+
+ space.setattr(w_A, w_x, space.newfloat(4.1))
+ assert w_A.version_tag() is atag
+ assert space.float_w(space.getattr(w_A, w_x)) == 4.1
+ assert cell.floatvalue == 4.1
+
+ def test_float_cell_turns_into_cell(self):
+ space = self.space
+ w_x = space.wrap("x")
+ w_A, w_B, w_C = self.get_three_classes()
+ atag = w_A.version_tag()
+ space.setattr(w_A, w_x, space.newfloat(1.1))
+ space.setattr(w_A, w_x, space.newfloat(2.1))
+ space.setattr(w_A, w_x, space.wrap("abc"))
+ cell = w_A._getdictvalue_no_unwrapping(space, "x")
+ assert space.str_w(cell.w_value) == "abc"
class AppTestVersionedType(test_typeobject.AppTestTypeObject):
diff --git a/pypy/objspace/std/typeobject.py b/pypy/objspace/std/typeobject.py
--- a/pypy/objspace/std/typeobject.py
+++ b/pypy/objspace/std/typeobject.py
@@ -36,15 +36,31 @@
def __repr__(self):
return "<IntMutableCell: %s>" % (self.intvalue, )
+class FloatMutableCell(MutableCell):
+ def __init__(self, floatvalue):
+ self.floatvalue = floatvalue
+
+ def unwrap_cell(self, space):
+ return space.wrap(self.floatvalue)
+
+ def __repr__(self):
+ return "<FloatMutableCell: %s>" % (self.floatvalue, )
+
def unwrap_cell(space, w_value):
+ if isinstance(w_value, MutableCell):
+ return w_value.unwrap_cell(space)
+ return w_value
+
+
+def unwrap_cell_iftypeversion(space, w_value):
if space.config.objspace.std.withtypeversion:
- if isinstance(w_value, MutableCell):
- return w_value.unwrap_cell(space)
+ return unwrap_cell(space, w_value)
return w_value
def write_cell(space, w_cell, w_value):
from pypy.objspace.std.intobject import W_IntObject
+ from pypy.objspace.std.floatobject import W_FloatObject
if w_cell is None:
# attribute does not exist at all, write it without a cell first
return w_value
@@ -54,14 +70,19 @@
elif isinstance(w_cell, IntMutableCell) and type(w_value) is W_IntObject:
w_cell.intvalue = w_value.intval
return None
+ elif isinstance(w_cell, FloatMutableCell) and type(w_value) is
W_FloatObject:
+ w_cell.floatvalue = w_value.floatval
+ return None
elif space.is_w(w_cell, w_value):
# If the new value and the current value are the same, don't
# create a level of indirection, or mutate the version.
return None
- if type(w_value) is W_IntObject:
- return IntMutableCell(w_value.intval)
- else:
- return ObjectMutableCell(w_value)
+ if not isinstance(w_cell, MutableCell):
+ if type(w_value) is W_IntObject:
+ return IntMutableCell(w_value.intval)
+ if type(w_value) is W_FloatObject:
+ return FloatMutableCell(w_value.floatval)
+ return ObjectMutableCell(w_value)
class VersionTag(object):
pass
@@ -274,12 +295,12 @@
if space.config.objspace.std.withtypeversion:
version_tag = w_self.version_tag()
if version_tag is not None:
- return unwrap_cell(
+ return unwrap_cell_iftypeversion(
space,
w_self._pure_getdictvalue_no_unwrapping(
space, version_tag, attr))
w_value = w_self._getdictvalue_no_unwrapping(space, attr)
- return unwrap_cell(space, w_value)
+ return unwrap_cell_iftypeversion(space, w_value)
def _getdictvalue_no_unwrapping(w_self, space, attr):
w_value = w_self.dict_w.get(attr, None)
_______________________________________________
pypy-commit mailing list
[email protected]
https://mail.python.org/mailman/listinfo/pypy-commit