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