Author: Carl Friedrich Bolz <cfb...@gmx.de>
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
pypy-commit@python.org
https://mail.python.org/mailman/listinfo/pypy-commit

Reply via email to