From c32dc47aca6e8fac152699bc613e015c44ccdba9 Mon Sep 17 00:00:00 2001 From: Mark Shannon Date: Tue, 2 Apr 2024 11:59:21 +0100 Subject: [PATCH] GH-115776: Embed the values array into the object, for "normal" Python objects. (GH-116115) --- Include/cpython/pystats.h | 3 +- Include/internal/pycore_code.h | 5 +- Include/internal/pycore_dict.h | 64 ++- Include/internal/pycore_object.h | 48 +- Include/internal/pycore_opcode_metadata.h | 6 +- Include/internal/pycore_uop_ids.h | 2 +- Include/internal/pycore_uop_metadata.h | 10 +- Include/object.h | 7 +- Lib/test/test_capi/test_mem.py | 4 +- Lib/test/test_class.py | 77 ++- Lib/test/test_opcache.py | 13 +- ...-02-24-03-39-09.gh-issue-115776.THJXqg.rst | 4 + Modules/_testbuffer.c | 3 + Modules/_testcapimodule.c | 4 +- Modules/_testinternalcapi.c | 20 +- Objects/dictobject.c | 464 +++++++++--------- Objects/object.c | 100 ++-- Objects/object_layout.md | 93 +++- Objects/object_layout_312.gv | 1 + Objects/object_layout_312.png | Bin 30688 -> 33040 bytes Objects/object_layout_313.gv | 45 ++ Objects/object_layout_313.png | Bin 0 -> 35055 bytes Objects/object_layout_full_313.gv | 25 + Objects/object_layout_full_313.png | Bin 0 -> 16983 bytes Objects/typeobject.c | 84 ++-- Python/bytecodes.c | 52 +- Python/executor_cases.c.h | 44 +- Python/gc.c | 7 +- Python/gc_free_threading.c | 6 +- Python/generated_cases.c.h | 54 +- Python/optimizer_cases.c.h | 2 +- Python/specialize.c | 51 +- Tools/cases_generator/analyzer.py | 7 +- Tools/gdb/libpython.py | 14 +- Tools/scripts/summarize_stats.py | 5 +- 35 files changed, 787 insertions(+), 537 deletions(-) create mode 100644 Misc/NEWS.d/next/Core and Builtins/2024-02-24-03-39-09.gh-issue-115776.THJXqg.rst create mode 100644 Objects/object_layout_313.gv create mode 100644 Objects/object_layout_313.png create mode 100644 Objects/object_layout_full_313.gv create mode 100644 Objects/object_layout_full_313.png diff --git a/Include/cpython/pystats.h b/Include/cpython/pystats.h index 2fb7723f583..e74fdd4d32e 100644 --- a/Include/cpython/pystats.h +++ b/Include/cpython/pystats.h @@ -77,12 +77,11 @@ typedef struct _object_stats { uint64_t frees; uint64_t to_freelist; uint64_t from_freelist; - uint64_t new_values; + uint64_t inline_values; uint64_t dict_materialized_on_request; uint64_t dict_materialized_new_key; uint64_t dict_materialized_too_big; uint64_t dict_materialized_str_subclass; - uint64_t dict_dematerialized; uint64_t type_cache_hits; uint64_t type_cache_misses; uint64_t type_cache_dunder_hits; diff --git a/Include/internal/pycore_code.h b/Include/internal/pycore_code.h index e004783ee48..6c90c9e2841 100644 --- a/Include/internal/pycore_code.h +++ b/Include/internal/pycore_code.h @@ -79,7 +79,10 @@ typedef struct { typedef struct { uint16_t counter; uint16_t type_version[2]; - uint16_t keys_version[2]; + union { + uint16_t keys_version[2]; + uint16_t dict_offset; + }; uint16_t descr[4]; } _PyLoadMethodCache; diff --git a/Include/internal/pycore_dict.h b/Include/internal/pycore_dict.h index ef59960dbab..5507bdd539c 100644 --- a/Include/internal/pycore_dict.h +++ b/Include/internal/pycore_dict.h @@ -11,7 +11,7 @@ extern "C" { #include "pycore_freelist.h" // _PyFreeListState #include "pycore_identifier.h" // _Py_Identifier -#include "pycore_object.h" // PyDictOrValues +#include "pycore_object.h" // PyManagedDictPointer // Unsafe flavor of PyDict_GetItemWithError(): no error checking extern PyObject* _PyDict_GetItemWithError(PyObject *dp, PyObject *key); @@ -181,6 +181,10 @@ struct _dictkeysobject { * [-1] = prefix size. [-2] = used size. size[-2-n...] = insertion order. */ struct _dictvalues { + uint8_t capacity; + uint8_t size; + uint8_t embedded; + uint8_t valid; PyObject *values[1]; }; @@ -196,6 +200,7 @@ static inline void* _DK_ENTRIES(PyDictKeysObject *dk) { size_t index = (size_t)1 << dk->dk_log2_index_bytes; return (&indices[index]); } + static inline PyDictKeyEntry* DK_ENTRIES(PyDictKeysObject *dk) { assert(dk->dk_kind == DICT_KEYS_GENERAL); return (PyDictKeyEntry*)_DK_ENTRIES(dk); @@ -211,9 +216,6 @@ static inline PyDictUnicodeEntry* DK_UNICODE_ENTRIES(PyDictKeysObject *dk) { #define DICT_WATCHER_MASK ((1 << DICT_MAX_WATCHERS) - 1) #define DICT_WATCHER_AND_MODIFICATION_MASK ((1 << (DICT_MAX_WATCHERS + DICT_WATCHED_MUTATION_BITS)) - 1) -#define DICT_VALUES_SIZE(values) ((uint8_t *)values)[-1] -#define DICT_VALUES_USED_SIZE(values) ((uint8_t *)values)[-2] - #ifdef Py_GIL_DISABLED #define DICT_NEXT_VERSION(INTERP) \ (_Py_atomic_add_uint64(&(INTERP)->dict_state.global_version, DICT_VERSION_INCREMENT) + DICT_VERSION_INCREMENT) @@ -246,25 +248,63 @@ _PyDict_NotifyEvent(PyInterpreterState *interp, return DICT_NEXT_VERSION(interp) | (mp->ma_version_tag & DICT_WATCHER_AND_MODIFICATION_MASK); } -extern PyObject *_PyObject_MakeDictFromInstanceAttributes(PyObject *obj, PyDictValues *values); -PyAPI_FUNC(bool) _PyObject_MakeInstanceAttributesFromDict(PyObject *obj, PyDictOrValues *dorv); +extern PyDictObject *_PyObject_MakeDictFromInstanceAttributes(PyObject *obj); + PyAPI_FUNC(PyObject *)_PyDict_FromItems( PyObject *const *keys, Py_ssize_t keys_offset, PyObject *const *values, Py_ssize_t values_offset, Py_ssize_t length); +static inline uint8_t * +get_insertion_order_array(PyDictValues *values) +{ + return (uint8_t *)&values->values[values->capacity]; +} + static inline void _PyDictValues_AddToInsertionOrder(PyDictValues *values, Py_ssize_t ix) { assert(ix < SHARED_KEYS_MAX_SIZE); - uint8_t *size_ptr = ((uint8_t *)values)-2; - int size = *size_ptr; - assert(size+2 < DICT_VALUES_SIZE(values)); - size++; - size_ptr[-size] = (uint8_t)ix; - *size_ptr = size; + int size = values->size; + uint8_t *array = get_insertion_order_array(values); + assert(size < values->capacity); + assert(((uint8_t)ix) == ix); + array[size] = (uint8_t)ix; + values->size = size+1; } +static inline size_t +shared_keys_usable_size(PyDictKeysObject *keys) +{ +#ifdef Py_GIL_DISABLED + // dk_usable will decrease for each instance that is created and each + // value that is added. dk_nentries will increase for each value that + // is added. We want to always return the right value or larger. + // We therefore increase dk_nentries first and we decrease dk_usable + // second, and conversely here we read dk_usable first and dk_entries + // second (to avoid the case where we read entries before the increment + // and read usable after the decrement) + return (size_t)(_Py_atomic_load_ssize_acquire(&keys->dk_usable) + + _Py_atomic_load_ssize_acquire(&keys->dk_nentries)); +#else + return (size_t)keys->dk_nentries + (size_t)keys->dk_usable; +#endif +} + +static inline size_t +_PyInlineValuesSize(PyTypeObject *tp) +{ + PyDictKeysObject *keys = ((PyHeapTypeObject*)tp)->ht_cached_keys; + assert(keys != NULL); + size_t size = shared_keys_usable_size(keys); + size_t prefix_size = _Py_SIZE_ROUND_UP(size, sizeof(PyObject *)); + assert(prefix_size < 256); + return prefix_size + (size + 1) * sizeof(PyObject *); +} + +int +_PyDict_DetachFromObject(PyDictObject *dict, PyObject *obj); + #ifdef __cplusplus } #endif diff --git a/Include/internal/pycore_object.h b/Include/internal/pycore_object.h index 0b17ddf0c97..4fc5e9bf653 100644 --- a/Include/internal/pycore_object.h +++ b/Include/internal/pycore_object.h @@ -265,9 +265,8 @@ _PyObject_Init(PyObject *op, PyTypeObject *typeobj) { assert(op != NULL); Py_SET_TYPE(op, typeobj); - if (_PyType_HasFeature(typeobj, Py_TPFLAGS_HEAPTYPE)) { - Py_INCREF(typeobj); - } + assert(_PyType_HasFeature(typeobj, Py_TPFLAGS_HEAPTYPE) || _Py_IsImmortal(typeobj)); + Py_INCREF(typeobj); _Py_NewReference(op); } @@ -611,8 +610,7 @@ extern PyTypeObject* _PyType_CalculateMetaclass(PyTypeObject *, PyObject *); extern PyObject* _PyType_GetDocFromInternalDoc(const char *, const char *); extern PyObject* _PyType_GetTextSignatureFromInternalDoc(const char *, const char *, int); -extern int _PyObject_InitializeDict(PyObject *obj); -int _PyObject_InitInlineValues(PyObject *obj, PyTypeObject *tp); +void _PyObject_InitInlineValues(PyObject *obj, PyTypeObject *tp); extern int _PyObject_StoreInstanceAttribute(PyObject *obj, PyDictValues *values, PyObject *name, PyObject *value); PyObject * _PyObject_GetInstanceAttribute(PyObject *obj, PyDictValues *values, @@ -627,46 +625,26 @@ PyObject * _PyObject_GetInstanceAttribute(PyObject *obj, PyDictValues *values, #endif typedef union { - PyObject *dict; - /* Use a char* to generate a warning if directly assigning a PyDictValues */ - char *values; -} PyDictOrValues; + PyDictObject *dict; +} PyManagedDictPointer; -static inline PyDictOrValues * -_PyObject_DictOrValuesPointer(PyObject *obj) +static inline PyManagedDictPointer * +_PyObject_ManagedDictPointer(PyObject *obj) { assert(Py_TYPE(obj)->tp_flags & Py_TPFLAGS_MANAGED_DICT); - return (PyDictOrValues *)((char *)obj + MANAGED_DICT_OFFSET); -} - -static inline int -_PyDictOrValues_IsValues(PyDictOrValues dorv) -{ - return ((uintptr_t)dorv.values) & 1; + return (PyManagedDictPointer *)((char *)obj + MANAGED_DICT_OFFSET); } static inline PyDictValues * -_PyDictOrValues_GetValues(PyDictOrValues dorv) +_PyObject_InlineValues(PyObject *obj) { - assert(_PyDictOrValues_IsValues(dorv)); - return (PyDictValues *)(dorv.values + 1); -} - -static inline PyObject * -_PyDictOrValues_GetDict(PyDictOrValues dorv) -{ - assert(!_PyDictOrValues_IsValues(dorv)); - return dorv.dict; -} - -static inline void -_PyDictOrValues_SetValues(PyDictOrValues *ptr, PyDictValues *values) -{ - ptr->values = ((char *)values) - 1; + assert(Py_TYPE(obj)->tp_flags & Py_TPFLAGS_INLINE_VALUES); + assert(Py_TYPE(obj)->tp_flags & Py_TPFLAGS_MANAGED_DICT); + assert(Py_TYPE(obj)->tp_basicsize == sizeof(PyObject)); + return (PyDictValues *)((char *)obj + sizeof(PyObject)); } extern PyObject ** _PyObject_ComputedDictPointer(PyObject *); -extern void _PyObject_FreeInstanceAttributes(PyObject *obj); extern int _PyObject_IsInstanceDictEmpty(PyObject *); // Export for 'math' shared extension diff --git a/Include/internal/pycore_opcode_metadata.h b/Include/internal/pycore_opcode_metadata.h index de525f72d35..aa87dc41387 100644 --- a/Include/internal/pycore_opcode_metadata.h +++ b/Include/internal/pycore_opcode_metadata.h @@ -1085,7 +1085,7 @@ const struct opcode_metadata _PyOpcode_opcode_metadata[268] = { [LOAD_ATTR_NONDESCRIPTOR_WITH_VALUES] = { true, INSTR_FMT_IBC00000000, HAS_ARG_FLAG | HAS_DEOPT_FLAG | HAS_EXIT_FLAG }, [LOAD_ATTR_PROPERTY] = { true, INSTR_FMT_IBC00000000, HAS_ARG_FLAG | HAS_DEOPT_FLAG | HAS_ESCAPES_FLAG }, [LOAD_ATTR_SLOT] = { true, INSTR_FMT_IBC00000000, HAS_ARG_FLAG | HAS_DEOPT_FLAG | HAS_EXIT_FLAG }, - [LOAD_ATTR_WITH_HINT] = { true, INSTR_FMT_IBC00000000, HAS_ARG_FLAG | HAS_NAME_FLAG | HAS_DEOPT_FLAG | HAS_EXIT_FLAG | HAS_ESCAPES_FLAG }, + [LOAD_ATTR_WITH_HINT] = { true, INSTR_FMT_IBC00000000, HAS_ARG_FLAG | HAS_NAME_FLAG | HAS_DEOPT_FLAG | HAS_EXIT_FLAG }, [LOAD_BUILD_CLASS] = { true, INSTR_FMT_IX, HAS_ERROR_FLAG | HAS_ESCAPES_FLAG }, [LOAD_CONST] = { true, INSTR_FMT_IB, HAS_ARG_FLAG | HAS_CONST_FLAG | HAS_PURE_FLAG }, [LOAD_DEREF] = { true, INSTR_FMT_IB, HAS_ARG_FLAG | HAS_FREE_FLAG | HAS_ERROR_FLAG | HAS_ESCAPES_FLAG }, @@ -1269,7 +1269,7 @@ _PyOpcode_macro_expansion[256] = { [LOAD_ATTR] = { .nuops = 1, .uops = { { _LOAD_ATTR, 0, 0 } } }, [LOAD_ATTR_CLASS] = { .nuops = 2, .uops = { { _CHECK_ATTR_CLASS, 2, 1 }, { _LOAD_ATTR_CLASS, 4, 5 } } }, [LOAD_ATTR_INSTANCE_VALUE] = { .nuops = 3, .uops = { { _GUARD_TYPE_VERSION, 2, 1 }, { _CHECK_MANAGED_OBJECT_HAS_VALUES, 0, 0 }, { _LOAD_ATTR_INSTANCE_VALUE, 1, 3 } } }, - [LOAD_ATTR_METHOD_LAZY_DICT] = { .nuops = 3, .uops = { { _GUARD_TYPE_VERSION, 2, 1 }, { _CHECK_ATTR_METHOD_LAZY_DICT, 0, 0 }, { _LOAD_ATTR_METHOD_LAZY_DICT, 4, 5 } } }, + [LOAD_ATTR_METHOD_LAZY_DICT] = { .nuops = 3, .uops = { { _GUARD_TYPE_VERSION, 2, 1 }, { _CHECK_ATTR_METHOD_LAZY_DICT, 1, 3 }, { _LOAD_ATTR_METHOD_LAZY_DICT, 4, 5 } } }, [LOAD_ATTR_METHOD_NO_DICT] = { .nuops = 2, .uops = { { _GUARD_TYPE_VERSION, 2, 1 }, { _LOAD_ATTR_METHOD_NO_DICT, 4, 5 } } }, [LOAD_ATTR_METHOD_WITH_VALUES] = { .nuops = 4, .uops = { { _GUARD_TYPE_VERSION, 2, 1 }, { _GUARD_DORV_VALUES_INST_ATTR_FROM_DICT, 0, 0 }, { _GUARD_KEYS_VERSION, 2, 3 }, { _LOAD_ATTR_METHOD_WITH_VALUES, 4, 5 } } }, [LOAD_ATTR_MODULE] = { .nuops = 2, .uops = { { _CHECK_ATTR_MODULE, 2, 1 }, { _LOAD_ATTR_MODULE, 1, 3 } } }, @@ -1316,7 +1316,7 @@ _PyOpcode_macro_expansion[256] = { [SET_FUNCTION_ATTRIBUTE] = { .nuops = 1, .uops = { { _SET_FUNCTION_ATTRIBUTE, 0, 0 } } }, [SET_UPDATE] = { .nuops = 1, .uops = { { _SET_UPDATE, 0, 0 } } }, [STORE_ATTR] = { .nuops = 1, .uops = { { _STORE_ATTR, 0, 0 } } }, - [STORE_ATTR_INSTANCE_VALUE] = { .nuops = 3, .uops = { { _GUARD_TYPE_VERSION, 2, 1 }, { _GUARD_DORV_VALUES, 0, 0 }, { _STORE_ATTR_INSTANCE_VALUE, 1, 3 } } }, + [STORE_ATTR_INSTANCE_VALUE] = { .nuops = 3, .uops = { { _GUARD_TYPE_VERSION, 2, 1 }, { _GUARD_DORV_NO_DICT, 0, 0 }, { _STORE_ATTR_INSTANCE_VALUE, 1, 3 } } }, [STORE_ATTR_SLOT] = { .nuops = 2, .uops = { { _GUARD_TYPE_VERSION, 2, 1 }, { _STORE_ATTR_SLOT, 1, 3 } } }, [STORE_DEREF] = { .nuops = 1, .uops = { { _STORE_DEREF, 0, 0 } } }, [STORE_FAST] = { .nuops = 1, .uops = { { _STORE_FAST, 0, 0 } } }, diff --git a/Include/internal/pycore_uop_ids.h b/Include/internal/pycore_uop_ids.h index bcb10ab723e..54dc6dcf408 100644 --- a/Include/internal/pycore_uop_ids.h +++ b/Include/internal/pycore_uop_ids.h @@ -109,7 +109,7 @@ extern "C" { #define _GUARD_BOTH_INT 347 #define _GUARD_BOTH_UNICODE 348 #define _GUARD_BUILTINS_VERSION 349 -#define _GUARD_DORV_VALUES 350 +#define _GUARD_DORV_NO_DICT 350 #define _GUARD_DORV_VALUES_INST_ATTR_FROM_DICT 351 #define _GUARD_GLOBALS_VERSION 352 #define _GUARD_IS_FALSE_POP 353 diff --git a/Include/internal/pycore_uop_metadata.h b/Include/internal/pycore_uop_metadata.h index 51206cd4ca2..0f2046fb3d0 100644 --- a/Include/internal/pycore_uop_metadata.h +++ b/Include/internal/pycore_uop_metadata.h @@ -136,8 +136,8 @@ const uint16_t _PyUop_Flags[MAX_UOP_ID+1] = { [_LOAD_ATTR_INSTANCE_VALUE] = HAS_ARG_FLAG | HAS_DEOPT_FLAG | HAS_OPARG_AND_1_FLAG, [_CHECK_ATTR_MODULE] = HAS_DEOPT_FLAG | HAS_PASSTHROUGH_FLAG, [_LOAD_ATTR_MODULE] = HAS_ARG_FLAG | HAS_DEOPT_FLAG, - [_CHECK_ATTR_WITH_HINT] = HAS_DEOPT_FLAG | HAS_ESCAPES_FLAG | HAS_PASSTHROUGH_FLAG, - [_LOAD_ATTR_WITH_HINT] = HAS_ARG_FLAG | HAS_NAME_FLAG | HAS_DEOPT_FLAG | HAS_ESCAPES_FLAG, + [_CHECK_ATTR_WITH_HINT] = HAS_DEOPT_FLAG | HAS_PASSTHROUGH_FLAG, + [_LOAD_ATTR_WITH_HINT] = HAS_ARG_FLAG | HAS_NAME_FLAG | HAS_DEOPT_FLAG, [_LOAD_ATTR_SLOT_0] = HAS_DEOPT_FLAG, [_LOAD_ATTR_SLOT_1] = HAS_DEOPT_FLAG, [_LOAD_ATTR_SLOT] = HAS_ARG_FLAG | HAS_DEOPT_FLAG | HAS_OPARG_AND_1_FLAG, @@ -145,7 +145,7 @@ const uint16_t _PyUop_Flags[MAX_UOP_ID+1] = { [_LOAD_ATTR_CLASS_0] = 0, [_LOAD_ATTR_CLASS_1] = 0, [_LOAD_ATTR_CLASS] = HAS_ARG_FLAG | HAS_OPARG_AND_1_FLAG, - [_GUARD_DORV_VALUES] = HAS_DEOPT_FLAG | HAS_PASSTHROUGH_FLAG, + [_GUARD_DORV_NO_DICT] = HAS_DEOPT_FLAG | HAS_PASSTHROUGH_FLAG, [_STORE_ATTR_INSTANCE_VALUE] = 0, [_STORE_ATTR_SLOT] = HAS_ESCAPES_FLAG, [_COMPARE_OP] = HAS_ARG_FLAG | HAS_ERROR_FLAG | HAS_ESCAPES_FLAG, @@ -342,7 +342,7 @@ const char *const _PyOpcode_uop_name[MAX_UOP_ID+1] = { [_GUARD_BOTH_INT] = "_GUARD_BOTH_INT", [_GUARD_BOTH_UNICODE] = "_GUARD_BOTH_UNICODE", [_GUARD_BUILTINS_VERSION] = "_GUARD_BUILTINS_VERSION", - [_GUARD_DORV_VALUES] = "_GUARD_DORV_VALUES", + [_GUARD_DORV_NO_DICT] = "_GUARD_DORV_NO_DICT", [_GUARD_DORV_VALUES_INST_ATTR_FROM_DICT] = "_GUARD_DORV_VALUES_INST_ATTR_FROM_DICT", [_GUARD_GLOBALS_VERSION] = "_GUARD_GLOBALS_VERSION", [_GUARD_IS_FALSE_POP] = "_GUARD_IS_FALSE_POP", @@ -736,7 +736,7 @@ int _PyUop_num_popped(int opcode, int oparg) return 1; case _LOAD_ATTR_CLASS: return 1; - case _GUARD_DORV_VALUES: + case _GUARD_DORV_NO_DICT: return 1; case _STORE_ATTR_INSTANCE_VALUE: return 2; diff --git a/Include/object.h b/Include/object.h index 96790844a7b..13443329dfb 100644 --- a/Include/object.h +++ b/Include/object.h @@ -629,13 +629,18 @@ given type object has a specified feature. /* Track types initialized using _PyStaticType_InitBuiltin(). */ #define _Py_TPFLAGS_STATIC_BUILTIN (1 << 1) +/* The values array is placed inline directly after the rest of + * the object. Implies Py_TPFLAGS_HAVE_GC. + */ +#define Py_TPFLAGS_INLINE_VALUES (1 << 2) + /* Placement of weakref pointers are managed by the VM, not by the type. * The VM will automatically set tp_weaklistoffset. */ #define Py_TPFLAGS_MANAGED_WEAKREF (1 << 3) /* Placement of dict (and values) pointers are managed by the VM, not by the type. - * The VM will automatically set tp_dictoffset. + * The VM will automatically set tp_dictoffset. Implies Py_TPFLAGS_HAVE_GC. */ #define Py_TPFLAGS_MANAGED_DICT (1 << 4) diff --git a/Lib/test/test_capi/test_mem.py b/Lib/test/test_capi/test_mem.py index 04f17a9ec9e..1958ecc0df4 100644 --- a/Lib/test/test_capi/test_mem.py +++ b/Lib/test/test_capi/test_mem.py @@ -148,8 +148,8 @@ class PyMemDebugTests(unittest.TestCase): self.assertIn(b'MemoryError', out) *_, count = line.split(b' ') count = int(count) - self.assertLessEqual(count, i*5) - self.assertGreaterEqual(count, i*5-2) + self.assertLessEqual(count, i*10) + self.assertGreaterEqual(count, i*10-4) # Py_GIL_DISABLED requires mimalloc (not malloc) diff --git a/Lib/test/test_class.py b/Lib/test/test_class.py index 0cf06243dc8..4c181414273 100644 --- a/Lib/test/test_class.py +++ b/Lib/test/test_class.py @@ -2,7 +2,6 @@ import unittest - testmeths = [ # Binary operations @@ -789,5 +788,81 @@ class ClassTests(unittest.TestCase): self.assertEqual(calls, 100) +from _testinternalcapi import has_inline_values + +Py_TPFLAGS_MANAGED_DICT = (1 << 2) + +class Plain: + pass + + +class WithAttrs: + + def __init__(self): + self.a = 1 + self.b = 2 + self.c = 3 + self.d = 4 + + +class TestInlineValues(unittest.TestCase): + + def test_flags(self): + self.assertEqual(Plain.__flags__ & Py_TPFLAGS_MANAGED_DICT, Py_TPFLAGS_MANAGED_DICT) + self.assertEqual(WithAttrs.__flags__ & Py_TPFLAGS_MANAGED_DICT, Py_TPFLAGS_MANAGED_DICT) + + def test_has_inline_values(self): + c = Plain() + self.assertTrue(has_inline_values(c)) + del c.__dict__ + self.assertFalse(has_inline_values(c)) + + def test_instances(self): + self.assertTrue(has_inline_values(Plain())) + self.assertTrue(has_inline_values(WithAttrs())) + + def test_inspect_dict(self): + for cls in (Plain, WithAttrs): + c = cls() + c.__dict__ + self.assertTrue(has_inline_values(c)) + + def test_update_dict(self): + d = { "e": 5, "f": 6 } + for cls in (Plain, WithAttrs): + c = cls() + c.__dict__.update(d) + self.assertTrue(has_inline_values(c)) + + @staticmethod + def set_100(obj): + for i in range(100): + setattr(obj, f"a{i}", i) + + def check_100(self, obj): + for i in range(100): + self.assertEqual(getattr(obj, f"a{i}"), i) + + def test_many_attributes(self): + class C: pass + c = C() + self.assertTrue(has_inline_values(c)) + self.set_100(c) + self.assertFalse(has_inline_values(c)) + self.check_100(c) + c = C() + self.assertTrue(has_inline_values(c)) + + def test_many_attributes_with_dict(self): + class C: pass + c = C() + d = c.__dict__ + self.assertTrue(has_inline_values(c)) + self.set_100(c) + self.assertFalse(has_inline_values(c)) + self.check_100(c) + + + if __name__ == '__main__': unittest.main() diff --git a/Lib/test/test_opcache.py b/Lib/test/test_opcache.py index 5fb4b815c95..8829c9a6d88 100644 --- a/Lib/test/test_opcache.py +++ b/Lib/test/test_opcache.py @@ -1044,20 +1044,13 @@ class TestInstanceDict(unittest.TestCase): c.a = 1 c.b = 2 c.__dict__ - self.assertIs( - _testinternalcapi.get_object_dict_values(c), - None - ) + self.assertEqual(c.__dict__, {"a":1, "b": 2}) def test_dict_dematerialization(self): c = C() c.a = 1 c.b = 2 c.__dict__ - self.assertIs( - _testinternalcapi.get_object_dict_values(c), - None - ) for _ in range(100): c.a self.assertEqual( @@ -1072,10 +1065,6 @@ class TestInstanceDict(unittest.TestCase): d = c.__dict__ for _ in range(100): c.a - self.assertIs( - _testinternalcapi.get_object_dict_values(c), - None - ) self.assertIs(c.__dict__, d) def test_dict_dematerialization_copy(self): diff --git a/Misc/NEWS.d/next/Core and Builtins/2024-02-24-03-39-09.gh-issue-115776.THJXqg.rst b/Misc/NEWS.d/next/Core and Builtins/2024-02-24-03-39-09.gh-issue-115776.THJXqg.rst new file mode 100644 index 00000000000..5974b1882ac --- /dev/null +++ b/Misc/NEWS.d/next/Core and Builtins/2024-02-24-03-39-09.gh-issue-115776.THJXqg.rst @@ -0,0 +1,4 @@ +The array of values, the ``PyDictValues`` struct is now embedded in the +object during allocation. This provides better performance in the common +case, and does not degrade as much when the object's ``__dict__`` is +materialized. diff --git a/Modules/_testbuffer.c b/Modules/_testbuffer.c index 5084bcadb10..cad21bdb4d8 100644 --- a/Modules/_testbuffer.c +++ b/Modules/_testbuffer.c @@ -2820,6 +2820,9 @@ static int _testbuffer_exec(PyObject *mod) { Py_SET_TYPE(&NDArray_Type, &PyType_Type); + if (PyType_Ready(&NDArray_Type)) { + return -1; + } if (PyModule_AddType(mod, &NDArray_Type) < 0) { return -1; } diff --git a/Modules/_testcapimodule.c b/Modules/_testcapimodule.c index e9db6e5683e..b2af47d05ee 100644 --- a/Modules/_testcapimodule.c +++ b/Modules/_testcapimodule.c @@ -3869,7 +3869,9 @@ PyInit__testcapi(void) return NULL; Py_SET_TYPE(&_HashInheritanceTester_Type, &PyType_Type); - + if (PyType_Ready(&_HashInheritanceTester_Type) < 0) { + return NULL; + } if (PyType_Ready(&matmulType) < 0) return NULL; Py_INCREF(&matmulType); diff --git a/Modules/_testinternalcapi.c b/Modules/_testinternalcapi.c index c07652facc0..d6d50e75b61 100644 --- a/Modules/_testinternalcapi.c +++ b/Modules/_testinternalcapi.c @@ -15,7 +15,7 @@ #include "pycore_ceval.h" // _PyEval_AddPendingCall() #include "pycore_compile.h" // _PyCompile_CodeGen() #include "pycore_context.h" // _PyContext_NewHamtForTests() -#include "pycore_dict.h" // _PyDictOrValues_GetValues() +#include "pycore_dict.h" // _PyManagedDictPointer_GetValues() #include "pycore_fileutils.h" // _Py_normpath() #include "pycore_frame.h" // _PyInterpreterFrame #include "pycore_gc.h" // PyGC_Head @@ -1297,14 +1297,13 @@ static PyObject * get_object_dict_values(PyObject *self, PyObject *obj) { PyTypeObject *type = Py_TYPE(obj); - if (!_PyType_HasFeature(type, Py_TPFLAGS_MANAGED_DICT)) { + if (!_PyType_HasFeature(type, Py_TPFLAGS_INLINE_VALUES)) { Py_RETURN_NONE; } - PyDictOrValues dorv = *_PyObject_DictOrValuesPointer(obj); - if (!_PyDictOrValues_IsValues(dorv)) { + PyDictValues *values = _PyObject_InlineValues(obj); + if (!values->valid) { Py_RETURN_NONE; } - PyDictValues *values = _PyDictOrValues_GetValues(dorv); PyDictKeysObject *keys = ((PyHeapTypeObject *)type)->ht_cached_keys; assert(keys != NULL); int size = (int)keys->dk_nentries; @@ -1784,6 +1783,16 @@ get_py_thread_id(PyObject *self, PyObject *Py_UNUSED(ignored)) } #endif +static PyObject * +has_inline_values(PyObject *self, PyObject *obj) +{ + if ((Py_TYPE(obj)->tp_flags & Py_TPFLAGS_INLINE_VALUES) && + _PyObject_InlineValues(obj)->valid) { + Py_RETURN_TRUE; + } + Py_RETURN_FALSE; +} + static PyMethodDef module_functions[] = { {"get_configs", get_configs, METH_NOARGS}, {"get_recursion_depth", get_recursion_depth, METH_NOARGS}, @@ -1857,6 +1866,7 @@ static PyMethodDef module_functions[] = { _TESTINTERNALCAPI_TEST_LONG_NUMBITS_METHODDEF {"get_rare_event_counters", get_rare_event_counters, METH_NOARGS}, {"reset_rare_event_counters", reset_rare_event_counters, METH_NOARGS}, + {"has_inline_values", has_inline_values, METH_O}, #ifdef Py_GIL_DISABLED {"py_thread_id", get_py_thread_id, METH_NOARGS}, #endif diff --git a/Objects/dictobject.c b/Objects/dictobject.c index 536746ca41e..58a3d979339 100644 --- a/Objects/dictobject.c +++ b/Objects/dictobject.c @@ -361,6 +361,10 @@ static int dict_setdefault_ref_lock_held(PyObject *d, PyObject *key, PyObject *default_value, PyObject **result, int incref_result); +#ifndef NDEBUG +static int _PyObject_InlineValuesConsistencyCheck(PyObject *obj); +#endif + #include "clinic/dictobject.c.h" @@ -624,8 +628,9 @@ static inline int get_index_from_order(PyDictObject *mp, Py_ssize_t i) { assert(mp->ma_used <= SHARED_KEYS_MAX_SIZE); - assert(i < (((char *)mp->ma_values)[-2])); - return ((char *)mp->ma_values)[-3-i]; + assert(i < mp->ma_values->size); + uint8_t *array = get_insertion_order_array(mp->ma_values); + return array[i]; } #ifdef DEBUG_PYDICT @@ -672,6 +677,10 @@ _PyDict_CheckConsistency(PyObject *op, int check_content) else { CHECK(keys->dk_kind == DICT_KEYS_SPLIT); CHECK(mp->ma_used <= SHARED_KEYS_MAX_SIZE); + if (mp->ma_values->embedded) { + CHECK(mp->ma_values->embedded == 1); + CHECK(mp->ma_values->valid == 1); + } } if (check_content) { @@ -821,33 +830,44 @@ free_keys_object(PyDictKeysObject *keys, bool use_qsbr) PyMem_Free(keys); } +static size_t +values_size_from_count(size_t count) +{ + assert(count >= 1); + size_t suffix_size = _Py_SIZE_ROUND_UP(count, sizeof(PyObject *)); + assert(suffix_size < 128); + assert(suffix_size % sizeof(PyObject *) == 0); + return (count + 1) * sizeof(PyObject *) + suffix_size; +} + +#define CACHED_KEYS(tp) (((PyHeapTypeObject*)tp)->ht_cached_keys) + static inline PyDictValues* new_values(size_t size) { - assert(size >= 1); - size_t prefix_size = _Py_SIZE_ROUND_UP(size+2, sizeof(PyObject *)); - assert(prefix_size < 256); - size_t n = prefix_size + size * sizeof(PyObject *); - uint8_t *mem = PyMem_Malloc(n); - if (mem == NULL) { + size_t n = values_size_from_count(size); + PyDictValues *res = (PyDictValues *)PyMem_Malloc(n); + if (res == NULL) { return NULL; } - assert(prefix_size % sizeof(PyObject *) == 0); - mem[prefix_size-1] = (uint8_t)prefix_size; - return (PyDictValues*)(mem + prefix_size); + res->embedded = 0; + res->size = 0; + assert(size < 256); + res->capacity = (uint8_t)size; + return res; } static inline void free_values(PyDictValues *values, bool use_qsbr) { - int prefix_size = DICT_VALUES_SIZE(values); + assert(values->embedded == 0); #ifdef Py_GIL_DISABLED if (use_qsbr) { - _PyMem_FreeDelayed(((char *)values)-prefix_size); + _PyMem_FreeDelayed(values); return; } #endif - PyMem_Free(((char *)values)-prefix_size); + PyMem_Free(values); } /* Consumes a reference to the keys object */ @@ -887,24 +907,6 @@ new_dict(PyInterpreterState *interp, return (PyObject *)mp; } -static inline size_t -shared_keys_usable_size(PyDictKeysObject *keys) -{ -#ifdef Py_GIL_DISABLED - // dk_usable will decrease for each instance that is created and each - // value that is added. dk_nentries will increase for each value that - // is added. We want to always return the right value or larger. - // We therefore increase dk_nentries first and we decrease dk_usable - // second, and conversely here we read dk_usable first and dk_entries - // second (to avoid the case where we read entries before the increment - // and read usable after the decrement) - return (size_t)(_Py_atomic_load_ssize_acquire(&keys->dk_usable) + - _Py_atomic_load_ssize_acquire(&keys->dk_nentries)); -#else - return (size_t)keys->dk_nentries + (size_t)keys->dk_usable; -#endif -} - /* Consumes a reference to the keys object */ static PyObject * new_dict_with_shared_keys(PyInterpreterState *interp, PyDictKeysObject *keys) @@ -915,7 +917,6 @@ new_dict_with_shared_keys(PyInterpreterState *interp, PyDictKeysObject *keys) dictkeys_decref(interp, keys, false); return PyErr_NoMemory(); } - ((char *)values)[-2] = 0; for (size_t i = 0; i < size; i++) { values->values[i] = NULL; } @@ -1419,7 +1420,7 @@ _Py_dict_lookup_threadsafe(PyDictObject *mp, PyObject *key, Py_hash_t hash, PyOb if (values == NULL) goto read_failed; - uint8_t capacity = _Py_atomic_load_uint8_relaxed(&DICT_VALUES_SIZE(values)); + uint8_t capacity = _Py_atomic_load_uint8_relaxed(&values->capacity); if (ix >= (Py_ssize_t)capacity) goto read_failed; @@ -1525,6 +1526,7 @@ _PyDict_MaybeUntrack(PyObject *op) return; mp = (PyDictObject *) op; + ASSERT_CONSISTENT(mp); numentries = mp->ma_keys->dk_nentries; if (_PyDict_HasSplitTable(mp)) { for (i = 0; i < numentries; i++) { @@ -1945,7 +1947,14 @@ dictresize(PyInterpreterState *interp, PyDictObject *mp, set_keys(mp, newkeys); dictkeys_decref(interp, oldkeys, IS_DICT_SHARED(mp)); set_values(mp, NULL); - free_values(oldvalues, IS_DICT_SHARED(mp)); + if (oldvalues->embedded) { + assert(oldvalues->embedded == 1); + assert(oldvalues->valid == 1); + oldvalues->valid = 0; + } + else { + free_values(oldvalues, IS_DICT_SHARED(mp)); + } } else { // oldkeys is combined. if (oldkeys->dk_kind == DICT_KEYS_GENERAL) { @@ -2464,17 +2473,19 @@ _PyDict_SetItem_KnownHash(PyObject *op, PyObject *key, PyObject *value, static void delete_index_from_values(PyDictValues *values, Py_ssize_t ix) { - uint8_t *size_ptr = ((uint8_t *)values)-2; - int size = *size_ptr; + uint8_t *array = get_insertion_order_array(values); + int size = values->size; + assert(size <= values->capacity); int i; - for (i = 1; size_ptr[-i] != ix; i++) { - assert(i <= size); + for (i = 0; array[i] != ix; i++) { + assert(i < size); } - assert(i <= size); + assert(i < size); + size--; for (; i < size; i++) { - size_ptr[-i] = size_ptr[-i-1]; + array[i] = array[i+1]; } - *size_ptr = size -1; + values->size = size; } static int @@ -2669,10 +2680,12 @@ clear_lock_held(PyObject *op) mp->ma_version_tag = new_version; /* ...then clear the keys and values */ if (oldvalues != NULL) { - n = oldkeys->dk_nentries; - for (i = 0; i < n; i++) - Py_CLEAR(oldvalues->values[i]); - free_values(oldvalues, IS_DICT_SHARED(mp)); + if (!oldvalues->embedded) { + n = oldkeys->dk_nentries; + for (i = 0; i < n; i++) + Py_CLEAR(oldvalues->values[i]); + free_values(oldvalues, IS_DICT_SHARED(mp)); + } dictkeys_decref(interp, oldkeys, false); } else { @@ -3059,10 +3072,12 @@ dict_dealloc(PyObject *self) PyObject_GC_UnTrack(mp); Py_TRASHCAN_BEGIN(mp, dict_dealloc) if (values != NULL) { - for (i = 0, n = mp->ma_keys->dk_nentries; i < n; i++) { - Py_XDECREF(values->values[i]); + if (values->embedded == 0) { + for (i = 0, n = mp->ma_keys->dk_nentries; i < n; i++) { + Py_XDECREF(values->values[i]); + } + free_values(values, false); } - free_values(values, false); dictkeys_decref(interp, keys, false); } else if (keys != NULL) { @@ -3595,10 +3610,12 @@ dict_dict_merge(PyInterpreterState *interp, PyDictObject *mp, PyDictObject *othe PyDictKeysObject *okeys = other->ma_keys; // If other is clean, combined, and just allocated, just clone it. - if (other->ma_values == NULL && - other->ma_used == okeys->dk_nentries && - (DK_LOG_SIZE(okeys) == PyDict_LOG_MINSIZE || - USABLE_FRACTION(DK_SIZE(okeys)/2) < other->ma_used)) { + if (mp->ma_values == NULL && + other->ma_values == NULL && + other->ma_used == okeys->dk_nentries && + (DK_LOG_SIZE(okeys) == PyDict_LOG_MINSIZE || + USABLE_FRACTION(DK_SIZE(okeys)/2) < other->ma_used) + ) { uint64_t new_version = _PyDict_NotifyEvent( interp, PyDict_EVENT_CLONED, mp, (PyObject *)other, NULL); PyDictKeysObject *keys = clone_combined_dict_keys(other); @@ -3608,11 +3625,6 @@ dict_dict_merge(PyInterpreterState *interp, PyDictObject *mp, PyDictObject *othe ensure_shared_on_resize(mp); dictkeys_decref(interp, mp->ma_keys, IS_DICT_SHARED(mp)); mp->ma_keys = keys; - if (_PyDict_HasSplitTable(mp)) { - free_values(mp->ma_values, IS_DICT_SHARED(mp)); - mp->ma_values = NULL; - } - mp->ma_used = other->ma_used; mp->ma_version_tag = new_version; ASSERT_CONSISTENT(mp); @@ -3816,6 +3828,27 @@ dict_copy_impl(PyDictObject *self) return PyDict_Copy((PyObject *)self); } +/* Copies the values, but does not change the reference + * counts of the objects in the array. */ +static PyDictValues * +copy_values(PyDictValues *values) +{ + PyDictValues *newvalues = new_values(values->capacity); + if (newvalues == NULL) { + PyErr_NoMemory(); + return NULL; + } + newvalues->size = values->size; + uint8_t *values_order = get_insertion_order_array(values); + uint8_t *new_values_order = get_insertion_order_array(newvalues); + memcpy(new_values_order, values_order, values->capacity); + for (int i = 0; i < values->capacity; i++) { + newvalues->values[i] = values->values[i]; + } + assert(newvalues->embedded == 0); + return newvalues; +} + static PyObject * copy_lock_held(PyObject *o) { @@ -3833,26 +3866,23 @@ copy_lock_held(PyObject *o) if (_PyDict_HasSplitTable(mp)) { PyDictObject *split_copy; - size_t size = shared_keys_usable_size(mp->ma_keys); - PyDictValues *newvalues = new_values(size); - if (newvalues == NULL) + PyDictValues *newvalues = copy_values(mp->ma_values); + if (newvalues == NULL) { return PyErr_NoMemory(); + } split_copy = PyObject_GC_New(PyDictObject, &PyDict_Type); if (split_copy == NULL) { free_values(newvalues, false); return NULL; } - size_t prefix_size = ((uint8_t *)newvalues)[-1]; - memcpy(((char *)newvalues)-prefix_size, ((char *)mp->ma_values)-prefix_size, prefix_size-1); + for (size_t i = 0; i < newvalues->capacity; i++) { + Py_XINCREF(newvalues->values[i]); + } split_copy->ma_values = newvalues; split_copy->ma_keys = mp->ma_keys; split_copy->ma_used = mp->ma_used; split_copy->ma_version_tag = DICT_NEXT_VERSION(interp); dictkeys_incref(mp->ma_keys); - for (size_t i = 0; i < size; i++) { - PyObject *value = mp->ma_values->values[i]; - split_copy->ma_values->values[i] = Py_XNewRef(value); - } if (_PyObject_GC_IS_TRACKED(mp)) _PyObject_GC_TRACK(split_copy); return (PyObject *)split_copy; @@ -4406,8 +4436,10 @@ dict_traverse(PyObject *op, visitproc visit, void *arg) if (DK_IS_UNICODE(keys)) { if (_PyDict_HasSplitTable(mp)) { - for (i = 0; i < n; i++) { - Py_VISIT(mp->ma_values->values[i]); + if (!mp->ma_values->embedded) { + for (i = 0; i < n; i++) { + Py_VISIT(mp->ma_values->values[i]); + } } } else { @@ -5296,12 +5328,6 @@ acquire_key_value(PyObject **key_loc, PyObject *value, PyObject **value_loc, return 0; } -static Py_ssize_t -load_values_used_size(PyDictValues *values) -{ - return (Py_ssize_t)_Py_atomic_load_uint8(&DICT_VALUES_USED_SIZE(values)); -} - static int dictiter_iternext_threadsafe(PyDictObject *d, PyObject *self, PyObject **out_key, PyObject **out_value) @@ -5330,7 +5356,7 @@ dictiter_iternext_threadsafe(PyDictObject *d, PyObject *self, goto concurrent_modification; } - Py_ssize_t used = load_values_used_size(values); + Py_ssize_t used = (Py_ssize_t)_Py_atomic_load_uint8(&values->size); if (i >= used) { goto fail; } @@ -6539,15 +6565,15 @@ _PyDict_NewKeysForClass(void) return keys; } -#define CACHED_KEYS(tp) (((PyHeapTypeObject*)tp)->ht_cached_keys) - -int +void _PyObject_InitInlineValues(PyObject *obj, PyTypeObject *tp) { assert(tp->tp_flags & Py_TPFLAGS_HEAPTYPE); + assert(tp->tp_flags & Py_TPFLAGS_INLINE_VALUES); assert(tp->tp_flags & Py_TPFLAGS_MANAGED_DICT); PyDictKeysObject *keys = CACHED_KEYS(tp); assert(keys != NULL); + OBJECT_STAT_INC(inline_values); #ifdef Py_GIL_DISABLED Py_ssize_t usable = _Py_atomic_load_ssize_relaxed(&keys->dk_usable); if (usable > 1) { @@ -6563,49 +6589,19 @@ _PyObject_InitInlineValues(PyObject *obj, PyTypeObject *tp) } #endif size_t size = shared_keys_usable_size(keys); - PyDictValues *values = new_values(size); - if (values == NULL) { - PyErr_NoMemory(); - return -1; - } - assert(((uint8_t *)values)[-1] >= (size + 2)); - ((uint8_t *)values)[-2] = 0; + PyDictValues *values = _PyObject_InlineValues(obj); + assert(size < 256); + values->capacity = (uint8_t)size; + values->size = 0; + values->embedded = 1; + values->valid = 1; for (size_t i = 0; i < size; i++) { values->values[i] = NULL; } - _PyDictOrValues_SetValues(_PyObject_DictOrValuesPointer(obj), values); - return 0; + _PyObject_ManagedDictPointer(obj)->dict = NULL; } -int -_PyObject_InitializeDict(PyObject *obj) -{ - PyInterpreterState *interp = _PyInterpreterState_GET(); - PyTypeObject *tp = Py_TYPE(obj); - if (tp->tp_dictoffset == 0) { - return 0; - } - if (tp->tp_flags & Py_TPFLAGS_MANAGED_DICT) { - OBJECT_STAT_INC(new_values); - return _PyObject_InitInlineValues(obj, tp); - } - PyObject *dict; - if (_PyType_HasFeature(tp, Py_TPFLAGS_HEAPTYPE) && CACHED_KEYS(tp)) { - dictkeys_incref(CACHED_KEYS(tp)); - dict = new_dict_with_shared_keys(interp, CACHED_KEYS(tp)); - } - else { - dict = PyDict_New(); - } - if (dict == NULL) { - return -1; - } - PyObject **dictptr = _PyObject_ComputedDictPointer(obj); - *dictptr = dict; - return 0; -} - -static PyObject * +static PyDictObject * make_dict_from_instance_attributes(PyInterpreterState *interp, PyDictKeysObject *keys, PyDictValues *values) { @@ -6620,56 +6616,24 @@ make_dict_from_instance_attributes(PyInterpreterState *interp, track += _PyObject_GC_MAY_BE_TRACKED(val); } } - PyObject *res = new_dict(interp, keys, values, used, 0); + PyDictObject *res = (PyDictObject *)new_dict(interp, keys, values, used, 0); if (track && res) { _PyObject_GC_TRACK(res); } return res; } -PyObject * -_PyObject_MakeDictFromInstanceAttributes(PyObject *obj, PyDictValues *values) +PyDictObject * +_PyObject_MakeDictFromInstanceAttributes(PyObject *obj) { + PyDictValues *values = _PyObject_InlineValues(obj); PyInterpreterState *interp = _PyInterpreterState_GET(); PyDictKeysObject *keys = CACHED_KEYS(Py_TYPE(obj)); OBJECT_STAT_INC(dict_materialized_on_request); return make_dict_from_instance_attributes(interp, keys, values); } -// Return true if the dict was dematerialized, false otherwise. -bool -_PyObject_MakeInstanceAttributesFromDict(PyObject *obj, PyDictOrValues *dorv) -{ - assert(_PyObject_DictOrValuesPointer(obj) == dorv); - assert(!_PyDictOrValues_IsValues(*dorv)); - PyDictObject *dict = (PyDictObject *)_PyDictOrValues_GetDict(*dorv); - if (dict == NULL) { - return false; - } - // It's likely that this dict still shares its keys (if it was materialized - // on request and not heavily modified): - if (!PyDict_CheckExact(dict)) { - return false; - } - assert(_PyType_HasFeature(Py_TYPE(obj), Py_TPFLAGS_HEAPTYPE)); - if (dict->ma_keys != CACHED_KEYS(Py_TYPE(obj)) || - !has_unique_reference((PyObject *)dict)) - { - return false; - } - ensure_shared_on_resize(dict); - assert(dict->ma_values); - // We have an opportunity to do something *really* cool: dematerialize it! - _PyDictKeys_DecRef(dict->ma_keys); - _PyDictOrValues_SetValues(dorv, dict->ma_values); - OBJECT_STAT_INC(dict_dematerialized); - // Don't try this at home, kids: - dict->ma_keys = NULL; - dict->ma_values = NULL; - Py_DECREF(dict); - return true; -} int _PyObject_StoreInstanceAttribute(PyObject *obj, PyDictValues *values, @@ -6679,7 +6643,7 @@ _PyObject_StoreInstanceAttribute(PyObject *obj, PyDictValues *values, PyDictKeysObject *keys = CACHED_KEYS(Py_TYPE(obj)); assert(keys != NULL); assert(values != NULL); - assert(Py_TYPE(obj)->tp_flags & Py_TPFLAGS_MANAGED_DICT); + assert(Py_TYPE(obj)->tp_flags & Py_TPFLAGS_INLINE_VALUES); Py_ssize_t ix = DKIX_EMPTY; if (PyUnicode_CheckExact(name)) { Py_hash_t hash = unicode_get_hash(name); @@ -6717,18 +6681,21 @@ _PyObject_StoreInstanceAttribute(PyObject *obj, PyDictValues *values, } #endif } + PyDictObject *dict = _PyObject_ManagedDictPointer(obj)->dict; if (ix == DKIX_EMPTY) { - PyObject *dict = make_dict_from_instance_attributes( - interp, keys, values); if (dict == NULL) { - return -1; + dict = make_dict_from_instance_attributes( + interp, keys, values); + if (dict == NULL) { + return -1; + } + _PyObject_ManagedDictPointer(obj)->dict = (PyDictObject *)dict; } - _PyObject_DictOrValuesPointer(obj)->dict = dict; if (value == NULL) { - return PyDict_DelItem(dict, name); + return PyDict_DelItem((PyObject *)dict, name); } else { - return PyDict_SetItem(dict, name, value); + return PyDict_SetItem((PyObject *)dict, name, value); } } PyObject *old_value = values->values[ix]; @@ -6741,10 +6708,18 @@ _PyObject_StoreInstanceAttribute(PyObject *obj, PyDictValues *values, return -1; } _PyDictValues_AddToInsertionOrder(values, ix); + if (dict) { + assert(dict->ma_values == values); + dict->ma_used++; + } } else { if (value == NULL) { delete_index_from_values(values, ix); + if (dict) { + assert(dict->ma_values == values); + dict->ma_used--; + } } Py_DECREF(old_value); } @@ -6760,9 +6735,9 @@ _PyObject_ManagedDictValidityCheck(PyObject *obj) { PyTypeObject *tp = Py_TYPE(obj); CHECK(tp->tp_flags & Py_TPFLAGS_MANAGED_DICT); - PyDictOrValues *dorv_ptr = _PyObject_DictOrValuesPointer(obj); - if (_PyDictOrValues_IsValues(*dorv_ptr)) { - PyDictValues *values = _PyDictOrValues_GetValues(*dorv_ptr); + PyManagedDictPointer *managed_dict = _PyObject_ManagedDictPointer(obj); + if (_PyManagedDictPointer_IsValues(*managed_dict)) { + PyDictValues *values = _PyManagedDictPointer_GetValues(*managed_dict); int size = ((uint8_t *)values)[-2]; int count = 0; PyDictKeysObject *keys = CACHED_KEYS(tp); @@ -6774,8 +6749,8 @@ _PyObject_ManagedDictValidityCheck(PyObject *obj) CHECK(size == count); } else { - if (dorv_ptr->dict != NULL) { - CHECK(PyDict_Check(dorv_ptr->dict)); + if (managed_dict->dict != NULL) { + CHECK(PyDict_Check(managed_dict->dict)); } } return 1; @@ -6804,23 +6779,27 @@ _PyObject_IsInstanceDictEmpty(PyObject *obj) if (tp->tp_dictoffset == 0) { return 1; } - PyObject *dict; - if (tp->tp_flags & Py_TPFLAGS_MANAGED_DICT) { - PyDictOrValues dorv = *_PyObject_DictOrValuesPointer(obj); - if (_PyDictOrValues_IsValues(dorv)) { + PyDictObject *dict; + if (tp->tp_flags & Py_TPFLAGS_INLINE_VALUES) { + PyDictValues *values = _PyObject_InlineValues(obj); + if (values->valid) { PyDictKeysObject *keys = CACHED_KEYS(tp); for (Py_ssize_t i = 0; i < keys->dk_nentries; i++) { - if (_PyDictOrValues_GetValues(dorv)->values[i] != NULL) { + if (values->values[i] != NULL) { return 0; } } return 1; } - dict = _PyDictOrValues_GetDict(dorv); + dict = _PyObject_ManagedDictPointer(obj)->dict; + } + else if (tp->tp_flags & Py_TPFLAGS_MANAGED_DICT) { + PyManagedDictPointer* managed_dict = _PyObject_ManagedDictPointer(obj); + dict = managed_dict->dict; } else { PyObject **dictptr = _PyObject_ComputedDictPointer(obj); - dict = *dictptr; + dict = (PyDictObject *)*dictptr; } if (dict == NULL) { return 1; @@ -6828,23 +6807,6 @@ _PyObject_IsInstanceDictEmpty(PyObject *obj) return ((PyDictObject *)dict)->ma_used == 0; } -void -_PyObject_FreeInstanceAttributes(PyObject *self) -{ - PyTypeObject *tp = Py_TYPE(self); - assert(Py_TYPE(self)->tp_flags & Py_TPFLAGS_MANAGED_DICT); - PyDictOrValues dorv = *_PyObject_DictOrValuesPointer(self); - if (!_PyDictOrValues_IsValues(dorv)) { - return; - } - PyDictValues *values = _PyDictOrValues_GetValues(dorv); - PyDictKeysObject *keys = CACHED_KEYS(tp); - for (Py_ssize_t i = 0; i < keys->dk_nentries; i++) { - Py_XDECREF(values->values[i]); - } - free_values(values, IS_DICT_SHARED((PyDictObject*)self)); -} - int PyObject_VisitManagedDict(PyObject *obj, visitproc visit, void *arg) { @@ -6852,74 +6814,101 @@ PyObject_VisitManagedDict(PyObject *obj, visitproc visit, void *arg) if((tp->tp_flags & Py_TPFLAGS_MANAGED_DICT) == 0) { return 0; } - assert(tp->tp_dictoffset); - PyDictOrValues dorv = *_PyObject_DictOrValuesPointer(obj); - if (_PyDictOrValues_IsValues(dorv)) { - PyDictValues *values = _PyDictOrValues_GetValues(dorv); - PyDictKeysObject *keys = CACHED_KEYS(tp); - for (Py_ssize_t i = 0; i < keys->dk_nentries; i++) { - Py_VISIT(values->values[i]); + if (tp->tp_flags & Py_TPFLAGS_INLINE_VALUES) { + PyDictValues *values = _PyObject_InlineValues(obj); + if (values->valid) { + for (Py_ssize_t i = 0; i < values->capacity; i++) { + Py_VISIT(values->values[i]); + } + return 0; } } - else { - PyObject *dict = _PyDictOrValues_GetDict(dorv); - Py_VISIT(dict); - } + Py_VISIT(_PyObject_ManagedDictPointer(obj)->dict); return 0; } void PyObject_ClearManagedDict(PyObject *obj) { + assert(Py_TYPE(obj)->tp_flags & Py_TPFLAGS_MANAGED_DICT); + assert(_PyObject_InlineValuesConsistencyCheck(obj)); PyTypeObject *tp = Py_TYPE(obj); - if((tp->tp_flags & Py_TPFLAGS_MANAGED_DICT) == 0) { - return; - } - PyDictOrValues *dorv_ptr = _PyObject_DictOrValuesPointer(obj); - if (_PyDictOrValues_IsValues(*dorv_ptr)) { - PyDictValues *values = _PyDictOrValues_GetValues(*dorv_ptr); - PyDictKeysObject *keys = CACHED_KEYS(tp); - for (Py_ssize_t i = 0; i < keys->dk_nentries; i++) { - Py_CLEAR(values->values[i]); - } - dorv_ptr->dict = NULL; - free_values(values, IS_DICT_SHARED((PyDictObject*)obj)); - } - else { - PyObject *dict = dorv_ptr->dict; + if (tp->tp_flags & Py_TPFLAGS_INLINE_VALUES) { + PyDictObject *dict = _PyObject_ManagedDictPointer(obj)->dict; if (dict) { - dorv_ptr->dict = NULL; + _PyDict_DetachFromObject(dict, obj); + _PyObject_ManagedDictPointer(obj)->dict = NULL; Py_DECREF(dict); } + else { + PyDictValues *values = _PyObject_InlineValues(obj); + if (values->valid) { + for (Py_ssize_t i = 0; i < values->capacity; i++) { + Py_CLEAR(values->values[i]); + } + values->valid = 0; + } + } } + else { + Py_CLEAR(_PyObject_ManagedDictPointer(obj)->dict); + } + assert(_PyObject_InlineValuesConsistencyCheck(obj)); +} + +int +_PyDict_DetachFromObject(PyDictObject *mp, PyObject *obj) +{ + assert(_PyObject_ManagedDictPointer(obj)->dict == mp); + assert(_PyObject_InlineValuesConsistencyCheck(obj)); + if (mp->ma_values == NULL || mp->ma_values != _PyObject_InlineValues(obj)) { + return 0; + } + assert(mp->ma_values->embedded == 1); + assert(mp->ma_values->valid == 1); + assert(Py_TYPE(obj)->tp_flags & Py_TPFLAGS_INLINE_VALUES); + Py_BEGIN_CRITICAL_SECTION(mp); + mp->ma_values = copy_values(mp->ma_values); + _PyObject_InlineValues(obj)->valid = 0; + Py_END_CRITICAL_SECTION(); + if (mp->ma_values == NULL) { + return -1; + } + assert(_PyObject_InlineValuesConsistencyCheck(obj)); + ASSERT_CONSISTENT(mp); + return 0; } PyObject * PyObject_GenericGetDict(PyObject *obj, void *context) { - PyObject *dict; PyInterpreterState *interp = _PyInterpreterState_GET(); PyTypeObject *tp = Py_TYPE(obj); if (_PyType_HasFeature(tp, Py_TPFLAGS_MANAGED_DICT)) { - PyDictOrValues *dorv_ptr = _PyObject_DictOrValuesPointer(obj); - if (_PyDictOrValues_IsValues(*dorv_ptr)) { - PyDictValues *values = _PyDictOrValues_GetValues(*dorv_ptr); + PyManagedDictPointer *managed_dict = _PyObject_ManagedDictPointer(obj); + PyDictObject *dict = managed_dict->dict; + if (dict == NULL && + (tp->tp_flags & Py_TPFLAGS_INLINE_VALUES) && + _PyObject_InlineValues(obj)->valid + ) { + PyDictValues *values = _PyObject_InlineValues(obj); OBJECT_STAT_INC(dict_materialized_on_request); dict = make_dict_from_instance_attributes( interp, CACHED_KEYS(tp), values); if (dict != NULL) { - dorv_ptr->dict = dict; + managed_dict->dict = (PyDictObject *)dict; } } else { - dict = _PyDictOrValues_GetDict(*dorv_ptr); + dict = managed_dict->dict; if (dict == NULL) { dictkeys_incref(CACHED_KEYS(tp)); OBJECT_STAT_INC(dict_materialized_on_request); - dict = new_dict_with_shared_keys(interp, CACHED_KEYS(tp)); - dorv_ptr->dict = dict; + dict = (PyDictObject *)new_dict_with_shared_keys(interp, CACHED_KEYS(tp)); + managed_dict->dict = (PyDictObject *)dict; } } + return Py_XNewRef((PyObject *)dict); } else { PyObject **dictptr = _PyObject_ComputedDictPointer(obj); @@ -6928,7 +6917,7 @@ PyObject_GenericGetDict(PyObject *obj, void *context) "This object has no __dict__"); return NULL; } - dict = *dictptr; + PyObject *dict = *dictptr; if (dict == NULL) { PyTypeObject *tp = Py_TYPE(obj); if (_PyType_HasFeature(tp, Py_TPFLAGS_HEAPTYPE) && CACHED_KEYS(tp)) { @@ -6940,8 +6929,8 @@ PyObject_GenericGetDict(PyObject *obj, void *context) *dictptr = dict = PyDict_New(); } } + return Py_XNewRef(dict); } - return Py_XNewRef(dict); } int @@ -6958,7 +6947,7 @@ _PyObjectDict_SetItem(PyTypeObject *tp, PyObject **dictptr, assert(dictptr != NULL); dict = *dictptr; if (dict == NULL) { - assert(!_PyType_HasFeature(tp, Py_TPFLAGS_MANAGED_DICT)); + assert(!_PyType_HasFeature(tp, Py_TPFLAGS_INLINE_VALUES)); dictkeys_incref(cached); dict = new_dict_with_shared_keys(interp, cached); if (dict == NULL) @@ -7118,3 +7107,24 @@ _PyDict_SendEvent(int watcher_bits, watcher_bits >>= 1; } } + +#ifndef NDEBUG +static int +_PyObject_InlineValuesConsistencyCheck(PyObject *obj) +{ + if ((Py_TYPE(obj)->tp_flags & Py_TPFLAGS_INLINE_VALUES) == 0) { + return 1; + } + assert(Py_TYPE(obj)->tp_flags & Py_TPFLAGS_MANAGED_DICT); + PyDictObject *dict = (PyDictObject *)_PyObject_ManagedDictPointer(obj)->dict; + if (dict == NULL) { + return 1; + } + if (dict->ma_values == _PyObject_InlineValues(obj) || + _PyObject_InlineValues(obj)->valid == 0) { + return 1; + } + assert(0); + return 0; +} +#endif diff --git a/Objects/object.c b/Objects/object.c index b4f0fd4d7db..60642d899bc 100644 --- a/Objects/object.c +++ b/Objects/object.c @@ -1396,16 +1396,16 @@ _PyObject_GetDictPtr(PyObject *obj) if ((Py_TYPE(obj)->tp_flags & Py_TPFLAGS_MANAGED_DICT) == 0) { return _PyObject_ComputedDictPointer(obj); } - PyDictOrValues *dorv_ptr = _PyObject_DictOrValuesPointer(obj); - if (_PyDictOrValues_IsValues(*dorv_ptr)) { - PyObject *dict = _PyObject_MakeDictFromInstanceAttributes(obj, _PyDictOrValues_GetValues(*dorv_ptr)); + PyManagedDictPointer *managed_dict = _PyObject_ManagedDictPointer(obj); + if (managed_dict->dict == NULL && Py_TYPE(obj)->tp_flags & Py_TPFLAGS_INLINE_VALUES) { + PyDictObject *dict = (PyDictObject *)_PyObject_MakeDictFromInstanceAttributes(obj); if (dict == NULL) { PyErr_Clear(); return NULL; } - dorv_ptr->dict = dict; + managed_dict->dict = dict; } - return &dorv_ptr->dict; + return (PyObject **)&managed_dict->dict; } PyObject * @@ -1474,21 +1474,19 @@ _PyObject_GetMethod(PyObject *obj, PyObject *name, PyObject **method) } } PyObject *dict; - if ((tp->tp_flags & Py_TPFLAGS_MANAGED_DICT)) { - PyDictOrValues* dorv_ptr = _PyObject_DictOrValuesPointer(obj); - if (_PyDictOrValues_IsValues(*dorv_ptr)) { - PyDictValues *values = _PyDictOrValues_GetValues(*dorv_ptr); - PyObject *attr = _PyObject_GetInstanceAttribute(obj, values, name); - if (attr != NULL) { - *method = attr; - Py_XDECREF(descr); - return 0; - } - dict = NULL; - } - else { - dict = dorv_ptr->dict; + if ((tp->tp_flags & Py_TPFLAGS_INLINE_VALUES) && _PyObject_InlineValues(obj)->valid) { + PyDictValues *values = _PyObject_InlineValues(obj); + PyObject *attr = _PyObject_GetInstanceAttribute(obj, values, name); + if (attr != NULL) { + *method = attr; + Py_XDECREF(descr); + return 0; } + dict = NULL; + } + else if ((tp->tp_flags & Py_TPFLAGS_MANAGED_DICT)) { + PyManagedDictPointer* managed_dict = _PyObject_ManagedDictPointer(obj); + dict = (PyObject *)managed_dict->dict; } else { PyObject **dictptr = _PyObject_ComputedDictPointer(obj); @@ -1581,29 +1579,27 @@ _PyObject_GenericGetAttrWithDict(PyObject *obj, PyObject *name, } } if (dict == NULL) { - if ((tp->tp_flags & Py_TPFLAGS_MANAGED_DICT)) { - PyDictOrValues* dorv_ptr = _PyObject_DictOrValuesPointer(obj); - if (_PyDictOrValues_IsValues(*dorv_ptr)) { - PyDictValues *values = _PyDictOrValues_GetValues(*dorv_ptr); - if (PyUnicode_CheckExact(name)) { - res = _PyObject_GetInstanceAttribute(obj, values, name); - if (res != NULL) { - goto done; - } - } - else { - dict = _PyObject_MakeDictFromInstanceAttributes(obj, values); - if (dict == NULL) { - res = NULL; - goto done; - } - dorv_ptr->dict = dict; + if ((tp->tp_flags & Py_TPFLAGS_INLINE_VALUES) && _PyObject_InlineValues(obj)->valid) { + PyDictValues *values = _PyObject_InlineValues(obj); + if (PyUnicode_CheckExact(name)) { + res = _PyObject_GetInstanceAttribute(obj, values, name); + if (res != NULL) { + goto done; } } else { - dict = _PyDictOrValues_GetDict(*dorv_ptr); + dict = (PyObject *)_PyObject_MakeDictFromInstanceAttributes(obj); + if (dict == NULL) { + res = NULL; + goto done; + } + _PyObject_ManagedDictPointer(obj)->dict = (PyDictObject *)dict; } } + else if ((tp->tp_flags & Py_TPFLAGS_MANAGED_DICT)) { + PyManagedDictPointer* managed_dict = _PyObject_ManagedDictPointer(obj); + dict = (PyObject *)managed_dict->dict; + } else { PyObject **dictptr = _PyObject_ComputedDictPointer(obj); if (dictptr) { @@ -1697,22 +1693,14 @@ _PyObject_GenericSetAttrWithDict(PyObject *obj, PyObject *name, if (dict == NULL) { PyObject **dictptr; - if ((tp->tp_flags & Py_TPFLAGS_MANAGED_DICT)) { - PyDictOrValues *dorv_ptr = _PyObject_DictOrValuesPointer(obj); - if (_PyDictOrValues_IsValues(*dorv_ptr)) { - res = _PyObject_StoreInstanceAttribute( - obj, _PyDictOrValues_GetValues(*dorv_ptr), name, value); - goto error_check; - } - dictptr = &dorv_ptr->dict; - if (*dictptr == NULL) { - if (_PyObject_InitInlineValues(obj, tp) < 0) { - goto done; - } - res = _PyObject_StoreInstanceAttribute( - obj, _PyDictOrValues_GetValues(*dorv_ptr), name, value); - goto error_check; - } + if ((tp->tp_flags & Py_TPFLAGS_INLINE_VALUES) && _PyObject_InlineValues(obj)->valid) { + res = _PyObject_StoreInstanceAttribute( + obj, _PyObject_InlineValues(obj), name, value); + goto error_check; + } + else if ((tp->tp_flags & Py_TPFLAGS_MANAGED_DICT)) { + PyManagedDictPointer *managed_dict = _PyObject_ManagedDictPointer(obj); + dictptr = (PyObject **)&managed_dict->dict; } else { dictptr = _PyObject_ComputedDictPointer(obj); @@ -1783,9 +1771,9 @@ PyObject_GenericSetDict(PyObject *obj, PyObject *value, void *context) { PyObject **dictptr = _PyObject_GetDictPtr(obj); if (dictptr == NULL) { - if (_PyType_HasFeature(Py_TYPE(obj), Py_TPFLAGS_MANAGED_DICT) && - _PyDictOrValues_IsValues(*_PyObject_DictOrValuesPointer(obj))) - { + if (_PyType_HasFeature(Py_TYPE(obj), Py_TPFLAGS_INLINE_VALUES) && + _PyObject_ManagedDictPointer(obj)->dict == NULL + ) { /* Was unable to convert to dict */ PyErr_NoMemory(); } diff --git a/Objects/object_layout.md b/Objects/object_layout.md index 4f379bed8d7..352409425ee 100644 --- a/Objects/object_layout.md +++ b/Objects/object_layout.md @@ -16,25 +16,23 @@ Since the introduction of the cycle GC, there has also been a pre-header. Before 3.11, this pre-header was two words in size. It should be considered opaque to all code except the cycle GC. -## 3.11 pre-header +### 3.13 -In 3.11 the pre-header was extended to include pointers to the VM managed ``__dict__``. -The reason for moving the ``__dict__`` to the pre-header is that it allows -faster access, as it is at a fixed offset, and it also allows object's -dictionaries to be lazily created when the ``__dict__`` attribute is -specifically asked for. +In 3.13, the values array is embedded into the object, so there is no +need for a values pointer (it is just a fixed offset into the object). +So the pre-header is these two fields: -In the 3.11 the non-GC part of the pre-header consists of two pointers: +* weakreflist +* dict_pointer -* dict -* values +If the object has no physical dictionary, then the ``dict_pointer`` +is set to `NULL`. -The values pointer refers to the ``PyDictValues`` array which holds the -values of the objects's attributes. -Should the dictionary be needed, then ``values`` is set to ``NULL`` -and the ``dict`` field points to the dictionary. -## 3.12 pre-header +
+ 3.12 + +### 3.12 In 3.12, the pointer to the list of weak references is added to the pre-header. In order to make space for it, the ``dict`` and ``values`` @@ -51,9 +49,38 @@ has its low bit set to zero, and points to the dictionary. The untagged form is chosen for the dictionary pointer, rather than the values pointer, to enable the (legacy) C-API function `_PyObject_GetDictPtr(PyObject *obj)` to work. +
+
+ 3.11 -## Layout of a "normal" Python object in 3.12: +### 3.11 + +In 3.11 the pre-header was extended to include pointers to the VM managed ``__dict__``. +The reason for moving the ``__dict__`` to the pre-header is that it allows +faster access, as it is at a fixed offset, and it also allows object's +dictionaries to be lazily created when the ``__dict__`` attribute is +specifically asked for. + +In the 3.11 the non-GC part of the pre-header consists of two pointers: + +* dict +* values + +The values pointer refers to the ``PyDictValues`` array which holds the +values of the objects's attributes. +Should the dictionary be needed, then ``values`` is set to ``NULL`` +and the ``dict`` field points to the dictionary. +
+ +## Layout of a "normal" Python object + +A "normal" Python object is one that doesn't inherit from a builtin +class, doesn't have slots. + +### 3.13 + +In 3.13 the values are embedded into the object, as follows: * weakreflist * dict_or_values @@ -61,9 +88,39 @@ the values pointer, to enable the (legacy) C-API function * GC 2 * ob_refcnt * ob_type +* Inlined values: + * Flags + * values 0 + * values 1 + * ... + * Insertion order bytes -For a "normal" Python object, one that doesn't inherit from a builtin -class or have slots, the header and pre-header form the entire object. +This has all the advantages of the layout used in 3.12, plus: +* Access to values is even faster as there is one less load +* Fast access is mostly maintained when the `__dict__` is materialized + +![Layout of "normal" object in 3.13](./object_layout_313.png) + +For objects with opaque parts defined by a C extension, +the layout is much the same as for 3.12 + +![Layout of "full" object in 3.13](./object_layout_full_313.png) + + +
+ 3.12 + +### 3.12: + +In 3.12, the header and pre-header form the entire object for "normal" +Python objects: + +* weakreflist +* dict_or_values +* GC 1 +* GC 2 +* ob_refcnt +* ob_type ![Layout of "normal" object in 3.12](./object_layout_312.png) @@ -79,4 +136,6 @@ The full layout object, with an opaque part defined by a C extension, and `__slots__` looks like this: ![Layout of "full" object in 3.12](./object_layout_full_312.png) +
+ diff --git a/Objects/object_layout_312.gv b/Objects/object_layout_312.gv index c0068d78568..731a25332b3 100644 --- a/Objects/object_layout_312.gv +++ b/Objects/object_layout_312.gv @@ -20,6 +20,7 @@ digraph ideal { shape = none label = < + diff --git a/Objects/object_layout_312.png b/Objects/object_layout_312.png index 396dab183b3e9b2f39edb49b033a612e66d3a09a..a63d095ea0b19ebebb86ccadb964278bcfe0648b 100644 GIT binary patch literal 33040 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 VMgU89l*m^wF@WUjRBn;ku|o^no_ILnQsw)Q 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 literal 30688 zcmce;2RN4h-#>h6NE*n<$X~RyIjg5*d*ZLR3U%NKqn6$W~U_M5H1km60T} zm7Vdt&VKjr-uM0d|Htth&u<*xzHwdGd4A5%=ly=I_jOZ8TXhRHD>Xq7TaKzJ>k-5n zcY>feLq&eBmO~oMnhGZSSA1auq-v2Ah?L5%7+a+;zzqZ&B8oa6(&EXKYws% zZ+p5y2bDChW}BRPM_xc&$s_Sk{3oi_8l3l@jy-D|6i2I~CVp;n-A9Q+iBHMgn)&`r z>i6%ouBbe~prD{iX3J3Vy4#0~gOhnr z;ZMw#jx>VqfB&HFA1#A_4;AOHZeS;WD|TDo_i*x8G;caN*ONbaqP)L^{0Xx@Pkfhn z9cxx=>Mz}r_-yEstFKnFsFf((r zii%2m-IsD7uLmO%b)5TO%IxrBxA$OS|&b_@C5G{DDv0YbJ_wZfOlg&>J zg}o=MHqhQw9_lHRAs$-2xpPZBrF}8$b6suiI->dcdHsD3pBaXShlf5F2pbq0+J%i6 z4olRT7?SJIxmsk~E_6F4=Jfa1k!qTn2Cn-<#b-^)waQz4UsJPIUS9rnV`Fvh>6%;7 z(T|31FJDgSODwPVLr)zzt27DsYy8?aoA zTiFl#3uxUlu-i^cOR%%EZ)TI)tQx^_eAWeaAE|rYM?2}J zrl#75*$=u>$+(Z#CdhexZ*=T_vEJ9$H(uU{6>oXt$rAyQ-MhsvbnS176;~>jcC7Z_ zIR7&>(VX@DO?p9ECMKi77t4z?UgzcNOdf4}N3MOlF1A@%qDmFBw!`l7C`uA`$v5IcA7JcNrUxTK}G8yOkVhP(>i93fse%t#)J zITbZEDya)yL8Ya#+^65Xd82gX$oe2PwIN%cZ8vl6KS_uj&;Nch05?fpMPsn1u7^$f zLf4^G?FZ)RVQPGe*a*1H8@i8F+V#(dig%ovT+4_zRjm(aZ%NVEo|e`&W_mw*+D=M@_nEETPt{rO6xO?r9b*Om2w^XZ7FU1``?+a4q>6_t6NlD2q zDhdn_-+Jx(^%4F%smBt_XRds@AK7pgwPwSP9Xpz{Ps%8(sGOM{{p2w{L`R;yNM2P% zt+9nyl=v*o`2BRYwzfL@`bzhu9B8+1ug7ABUY#hhoOr!BGa`PbHptjkN!jhP&@aOs z`<)1GZf?hdW3%VTgR*`T(?ieSKVq@uD4RUIypgf7K}SNEs+yWI>n_m?Xdd$PJRraD zo!+e2iDI|ubC3GSy>%uvwR2s0?#V}^1#)w9(_FfThK7nbYCb))7QZywS=1xu<%Y|e zU!LpDJ6%J$L*~+ECMKr&;f$O;p1;mAv$1_lrx6www*QpB9!nJX^r@|!Y7?{N1dVx# zrQ=)2isc&-@{3farl!@LBTGw5tRox;oY!$)ndg39P|%WNq0sfhZ4;qmWF+9e(DC-I zR(AK#*3{G1)*H06w4OVBVWOj>bFP0Me_*YbmzRy5T@C8xg$oy6zj;%Wee&g=Q=8&HJgK0iNy{dCO@ZZR<$ui4S6?w9U;BO@mUYqLK* zJ{rA05Btv-yQ_?`Vr5ZSetBA-fq~&`)%6XNzke&8I>ohvon04~owf2n#+{atF`yw@ zu>X5Q^icOpX^Fjilb$@e@zQ;w!{fj#o-0{G$xjOn-Q3yPd1`v7W&Zbj1q!02rG@-a zTU*=Y*Q&K@eB1nQ+^Bx@W>a={cC3PLd9$&K3S|(L!{_Je717n}^78Ux54s68y?r~? zySlP{E#>Az-rlmwj~)$-r=Ty+{y@oh%q%V4@yz-?CEe-H+|zrcr0R+<_LE)Cti*YJ zQc_a;=cap-o3`CtNB$1+I5pMs_m8)PUq{x7kHtr`0s}WRHa70TZXFsPuKx6iMcF?_ z*mw<5T3V`OYDz6FEv=TK=i%XTNJC@OlP6DR=Esja;03fC9O2{LFKg=RD7TB69m3+E zDbj7*b}gJ;M(Ch28n$|TSDy6jM+@J+zM4S7?}^s^H6AN*35$!1gx~cIG##G{hqWg0 z+O&cm95!rAD#FRh$xn^%+OubS2`*95tq~k@+ddoj_$kqL=?fdRv}Z{NO-kH2Hvg#9bmkB6A|{Q15#1cN3`c$yS9exkh@P5)|Ptyo@@|I z4`NEVk{RG7N#%H*6c8!zHNW{bkd|7I_Jnfm4TY=hChdf zs4VL1>dN`f%u2=Fy-O~6Zhn6CaC=7Ij~_Q4KjvGStf9$${@md2dk&HBX;O5d+yBpJ zA?XdGyLMIKVG#tLcUp&vKs1G4SL;(zQPG0%)926cl091D^XC$<%4_zCu99a?vLYgy zq75RVqLjpyr}-Q^&~~sv>zd;BPAv>3_P=`|V^ZSGuc@iYz|LwJDrrhS7(B`w=vHey z%8xo>$Ds{S@hGBnXE<1|A+Tv)zkA2@Su1|O?)Zmpg~U6C#>RmW5i|t37#2P=jQRQb zPBX)XOVh2#Y(6~RXq08To+$QRl}C|`1YENFm|fM|%SklnTWKU!+?RIb;o|xlaQE8r zF+DxKucM=(ID<)8E^JiFjvNcIqUuJpufsukMSKzxQ7>G7DgqsxJ$qJ}UQqk7;IRa% z`ijM2VN=`%`vGTv`NbjL4;>v(q#Qms7xiAz7#J9E_Bn53LrdI~^~^W(JamXCux$@+ zYj022x-a$o+*{U?LI8$TfVz$K6$?Mugm&-d=Shx=+IDaMMV|9^cKUy2$9|4}dN$)B zbz2_`yng-qNR;aWhc9$QeXg`XoM{iucH!Mw6GrFtZ=W^yTg zD9^I~8@bfaZRmcyPo!~kb2}3F3U_5rw(2c)DClJW0mx)#VWA-8=fAF9ullO-&6~W- zGq$J>YjHq7=2+Ax9|`$ehbV)K0<5>F;It>mdR`-OCQdoA$7Ax;de!z~Igd#K6{FaD zLBc50^R zeL~-d6b+OddBIC4BphG8#+fah$?Nf6a2ByB-+{L$(cQd0KfvQM-MhQ!i`S;7U5j75 z;5m4ZRbgpzEuJ-dy5h{TE!0N=|Os7b@wj z?9V7{^w4*81qb~Qn*KjNx&OQN&CneOw(&#;SxhttOGxOo=u>|B^od#Cd;c9Vi}1?I zL(0m^hm4JlTTX)sc&+t5Zq(5E>C@?iA%X?|6 zK6vX*;MNnB4G*I7a&s%wbRRLWQ+jPYBIthl^yyeB2UdG~ds;d=3VnV3fVvvo5P;o!D5gVEj!&crK=>B&lGLdA_} zJO;wW`HjuZ!~OgSTwK-b*HpgCGuz2qd{X=OhfLeIZ*X^aUz+)7VNz(%Qd(A)^z`Y| zp|mG9WGW0@ykG962hO)nBBkKEU<1pZF;@-!7Xay?3aWK@)% zqh*J5oF*kusfnrS8Jq#Cty}A`v8Qr;mo-|Fc4eJ!ebO*yVPatLm^=buI|SV`4uj@4HG zz(6GtCUR4|489u})9T^I>3VM4uHJHbPhG5^@pz|3f&C}F;_uTy(BvU>zMCXOfAi^p zxw$#v_xiP($%zx>LIDELqap@e;gt6dA`zBQjG_qBBbES`Y~|axS0Q3M{?y*22Wg|)xbg70b5EKroSh}M(bKnN7>l8m z&(F`F`cmlNu{3|NETPwUYWk->iN}DHuid=4adCDmq_}uL$vx58s!_S#+&szdHr#gf zf;_f61#xHh34z<_g)~I2%b<4WmoG}HsvA%iRu|s;n(r3_dK_5PR#$&2<(tv-#S>=@ z`|Pm3KE1=2!XwJc{!XK9>H0%mC3~l5X7Zf+6iNJb^XAPNS0n7+tFa%PE?p9^wjM0% znZScO|F!b!<0ns4&=!@IZ$EgTH9I>?7F?7*#covQ7Idogw6vhY3*G8lw{E>18>!h81IBAOqmMg#>7VX^js z;sl3<@yBVZ$a&5385$a{In?Vj$9}TZBNA*}r9Ohw_h+ZFGT5;Io>9r1t)-fKwAN|(y;*R{K$Uz?p=8K^6Uw9O-=8S zJV~ax@oxHey9gW?0iNXidXKrd$^eDZm|+Pi-I?#%xw&`>DH?bE^YJv>?VRVZ@%6E<8>*_V zk`2`MV>UaU=*Eo%A+c}YpFlEFPYFsG$^Gc%n(~6ryE#k_X=0eZ%jrti0FyUFMxGLxEWI9%bd{ zj=l104k_K(oTPDAl#Y3W?3H+a_V{hmF8UPea7=uIpC#KV-#Pj12&mdT#>u7 zvS4jvYipEI;raU_1qn|{1u z=L(Nh|law}qH1R@j!iK6v149(=)^E zCZ!(Y=ibF{L_u^N?Tn;<>0a5|N?-X~jz-L)JQR=j^y^#P)l;By6z>z{NI5|sA{!qc zA632Wy_}rnVIMCLtBfu)t)oY;nuyea3R0rk-M(|@%%^;-q1IFyh%h}iW_7X70PyXf zaJIRl8=R(xbVs{N=+Ql1p`ct`UGXt{;kpt3`750rvasN+{CzfE07sf65as3aXDluI zhKEi2pR!LbQMzx~u%YRJOp&&9RE7QM_PnAZ)iY;!)~sE7+Qufxv3!B@@ZrNbT{K?4 z05;UZl_ah~DSMdwtHL4-cRf6~H8_+O5{3Ujk}aG>Ml z6w4G)QC01~?^vF$zd1Co?q)Qe+S~ims)_PGli+AcO_#oaJ z>#2tC*tLU~2SQgLo_OqDYwfVRi*}E)vUYo)Ja7y;(7-V8Ky_y)Gm#6a#5SWApKVfL z%LpYW4~H4NVngNcHzhzlY}(_U!Yn{aD4V=R=ST2(@~qx4dCz{bR*b`iqNCmT_U#P% zVr5s?4zw~zS~aNLr_Y_EBsvT1w&RA$T^ujtzBM2qpxE=bz?IcycdR#MngL=w@)t1oUV8ts}l|1^PkkQLHD6xmE@D%0p-EmavA(2G9`uo{Q2{`XjVV5e%0c@c^_I^AK{K#&|Y_m zh=kUj5r6wY#`r=AHSrwi-1NCkq}lQIy9PojB=A2!+9>FoQ4Trrs6e!?lhYo2PGnx5 zc-;N_t-l7|WXL7`k^0JiA}QB>B1Eh_?P5qI>PMuB$P@Atumg61azQOQnxc14&Pz%w zLAKYqM>vQ`U4ZqeYZ8&lZq!&%_EFu?-T7YXxMxo-@SX6oxp1qDtR`(OH%RKxwnd7y&pb&_}+MPC}!^U zrmb65w6))T*__j}4DmZw#$6Pmk!M8?fZ680ZF2;xxF*2VxRy>}%fK0)~$;p{?(!*oFq{HXi?-JxRP`3BE{Lox;sIRXNU=Oy` znnPeSVxc4G-j6dfG&az1Jch#%92CTrXQC-3C1qf0s{TJwft(Iwz`B|7?)yYDbk~4i zxG@soqLu0cPJHU@)B!f!wR?Bih2C;4oSU=O)`D$!8_urt12BV3fRAnio_e}?jA!rO zXl#Rx1V9%ZT?;rb34O7IyLfr8tAsKi#&%+0W7AMp#x*@h6N+j$>k3juqT%PypW9BX zB?(9AWT~V8VLlIalb0ZG2OA;<9Gbiu{J2@cH>~Wv3m-U7?U}r;KQ|*H1c`k(1|`2x zWmqNHTFXm!$EHLDPGS#s10)Mh@Jb4T z6gAO!v9bZpPl}6~6A}_k^3JS>Q9ux|GL*3T=!3(uQgo+(G{uoZMSOfbA-kHE2BnQu zN$cR1(6O^`!LyqG^9NLngG5T8vL=?kdg%-!A9LzKd3F|k)-7@V_TvZT*9F^p>qH+C zi^bl%muyjSh1B-#!t{9}NwucfZRC#5xl^b7(2n|nvI#$MDG`%g!unnOVK@?mtS>F- zh9{ETE z>-Uk)rGZAqk}*zTMRCXzr2RqKqRPs(8e%`wGct^YTXDS0%P)Z%s!@n8^3~IgeE%EumVy8haoo28xIhqi+|C2%&Yf%S=uiO`^c$X&E^7i5+GF3j z2mRk+oHyNxc9@$s8oTz<2Tkd6|B zMU%42k{#J6L-2c+3%>^1-#NA~jt&$Yb9?g7EP#S9st%;3!^XzjflPNnTMxXlym)$| zr>tPbEXFF_K;Q~YFy{drF6Vj{%gk*#UAsyeSKB{cihCvnen$8K+e))m{Gu^y*Ek*n zZ=)F+nja~-08by*(P?Ij=w?oT`cx5T4&o3qSp1bgW722OojZf-Jk(pUszI-4701gi zbICs;fdfZ;pb^6OS^}2bN4EO6%=)5e`Ug&&IFXi?b_zHP|46%3>YS4F%h_O{ObfDG zpoxeIjurN5FbEbG>>;wxB_?wEt!5QAZ<6waO(_hy;sy%WYdNjh`1nwe(W+Ok))W4n zLiN5Zk!tH8u#G~zf%g5%eDEyb^TtW_qjFnfJ!GU`CVg zwVC}O1HjblhK4HiT5_-SSsQF`lu(8IP1?)Ks;X}sn6Hd>irbt&PsRn@X8@uCBRf8@AEXLDGYk zO%0?#s=d&aC?~x)F??Mp(>PIRe;DR-!4%jtEiJ8IeEbelFNTkfP7*8cbC7cD4k~MF zYxrtZ{j0zA_nM!D@lEb{GqdW6W0EX1jg*(ZS)UiFlDU4*JaIzOmq`+XMo0kE$B zy1=>6xIr0cmxA;uNQWCpY*O_joT!(V#;WtJ-sqT_ZAR$>^HxFuLp39nWa1PyKPlT? zxF8DFaab92^Vox4eak0kjjF1uq*{itHgqNc76W)!w9TY}MC%P^lN>h;F<3w26nW+?1vHNEuPSSOp&&OEXg4u445%f_x`?gL^y|>X;#;lFZZXXr_;cao+F$=y=fCK zdM(7#6d;W^Wfm?jE<8L!A|j99E3C$2L#3mOxk4s4#s0#f*0r(OIWRa#PuBvM?)8Tc zQRshZxDMBEwHtXZm%FL_7gCX)oSbax{WVMHyZ7qyoa6(EEu; zx6h`Ti!^Cw$G)`U|8|-6CzAGb42?0-O$FKsg*3j|iw`h#;v2PsP0Pc>;~{+epIAL* zA@u9Rhg>4&rSk!7*D6Bq-)Dp3cX4G2ZR+-zH*z5if8B`xzybc|SL_abp_nmN&i?j2$KSRuxYoNXVTg2!;GK^lA zRONSx3K8GGf4_U>hkzaPzpx5}g`c#tgDjS#zWYn`d7wWApd;Ma%Kmgnr&D9y`t?>;Yh_hcUA-p5DC=_8?0;t*lnv~R%a8uo%mGevl)Kag z9i3Z~EO|M9KIH@7Ub=K?X2&keOm5OX)zQ_ZL*YK{=tx6PPmkh}bn@jT6sjX&Ek@a9 z$FsEHLVyiI>ZJjxK7^wNyxv*nC7o?i5e{Bh>ZHkYIm_qG|KvaXYV@Hv1hi*nX7(^O zH4ymiRLzYoIqfuFt4sgJS$Y?&3xoe1OW{sDGHeygvurpclkl`iob2p?M@c$V_Me|`BrVNt+qT`h zcP|(~on!FbgX;walIZ((?5&lR1gU?3=m^)dA@G94)MnOwRDdcgD=Qw0)4M>&ry6~L zGXGm*GaK{g%tRu@J6=&y?UvK8r)FkeMJTKU;iyj8?z0>X@JN&~dW1T;dB=WQ!fEQ; z5kN5NdLiR+L^}kF)SV!Aku@GfpxE{2HfZ|+mK_}(aQfGNE^>^(&2?xrf%rh3@D=s} zL*a&nA}%hzXU`s>{oUu@Cxr29+^IyTf~V~*+@E{n4GfVM?$@6R9oS&^aWjQ5vQt}T zUhR*OG2%svs;k16^Xk>B&NojQLdmWqh3|<$m`$Uj_5hP__%9iHdU#Y;S0~q5!YPB5 z=H%+?5Buq_H{8?HvqxO~Ff0KkG4sv5yu7h-aU_l){NUss;@ifJ$QGbPwT~)(PVpYc6PS)nSlBT@~{EueL#wpW1kHz902%0;oIX6JhAyps8&b+cS5B< zuE4QcDtQR*zl}w!36@3C6LcmsD+@h?0^yXUrQh$X^J;6Ar-$3gYy{X_Cl-W5;fh)6 z+>@tIskOA;emp3K=%D|V7$rRE>xp(g#r2A>p{_%fD zli-jP6$$<|Jb>U`0XG{P8o0siqty7ofrOqX%6dw$u(CGJc%nN)Xh{JK)H65d0I#@Z zA|k-X##T~j_^Q61o?w-`>{>8K<#%H<%P-T393cWNf2wYI-z9YZ>Gu|vaBq#<7T{E<3tVsh)jg9kbm0?~8?wk{o=+^RbX zZ%d{Cp+Qsb5IVR9IyzW%F@R}1#l<6`EZcx?gG4GKkfN+iz9p*nf3#?Ld6I$JwuQVB z^L`IVvk%6WX#^a|I6TG??{auh*3vm$dA4MANKWQ?ZvSZ;?9cXaEXbRzP(-CP<0RID zAs|^5c;iN$T^RVPNo{@on-~D0jfrdreOJ6)$38RSz2^}-h>VWD4ye}lne*SXATn%- zL|PT#(_JwOk_Xw@8!Yp7f?r>m~5?|goq9@(C?*x#h$heal-4-k?l zWN>JniqK%g!^2GwWH=$e7HBIRa8gL!}@dq7iqLgQAe}7&|+=66kR5MB3~gwG^ZmO2mZ_Em>Q6;$==m zdm_M563N`aA_MY~7kKSJw~dW0gk|qJ(itJ`6m(hh{fU0}d}j>@2jCFgN=OKYm_)TY zmG1p;5Gb(u!v{qW%$C$+ob!u|JpBCYAuK(e>DU1GiXEvD+s}m#n+RxZ2qLP4xnZqH zAs2}3L!+|TfddO+h2c87>GJ$`LKz|0i39;4GSK+8sR>Ld46K0li{VNV{`I!-`*-i| z$h>1MM8K5vcu=nhzsX6b5}$r_3o_)O`QRWUj>CEkJW7h_cHwm)}I@FYbY4OUBNSb_FU$ zpT9Ht`22sSFy%2D7=<|bfk5Etvvm1`UhJ09DY%*UC zhGEl@$wF%Gb9|O?pyAR`Zr^^F)e=ph9+?_icJ|$K24g6h4^vVCAZ&H8D%kA@&XSdr zBNcoy8-VB^Z8%O=A9i=_zVlICJ}HLAssvFy2LUQxqrBSDp#n%W^CF8}r;*ez_xrAZFAJlYVxm?Xwzd~ zwr|=lS_>UV7d{1Q5$WjQgawd+jZbY24IAO&)gsE(oOMFlzPFqmJ$L8s-LD|Ew=FrIu9hopzv@?BL1KowX#2~Sww41(~c>t9X)y&aj;*%e_sT` zCHx?u0i_@Yy%q@)@& zT8<$3p%W+AE?&IoEL;#%!~pwn6G{dd8i)56SY55yRq9C(v4`mg?u-HWlOV812tU9% zupF*^`wY~YkTl69@8U!udD&#Nprqsg0qh_IdSs}6AcoIUVg@x8?aUk6dj*my$sZk8O%t|~;Q zETi$UF|Es&FGJ}P{ETca6b{mNC~+D5fV&>}5sg)#qhn-ZBHa*eG|{#^OVxvJ!wgv_ zdDliqM@c&Y&+%puawJ-3&!0aE*Qc$_%l+WRZ|msfdd>Ow?~er+CG7?zB+rB386aM- zEY?G9)l=%({$l~_lhNLvrNeADO}Y?M8wB$&>wA{uI=fhOINem{fna#5EloFg>p&S3 z3(JBLhri8Em;t#SQ)f!vl;^Y;U2>&L_>IJt!vAJu^mxS#PP%a@+Jr(U4MXzv3>y4LHuUcZ$G^^P*y$h(cu%QMXfPjDi*>FJGzmANAAQG8rR+4XeA5a)N z`9tg<62dC;90q*M^P0P0R&j;ht|Qa4FIIWl^v|7?$B!$kswjZ0$jEeE1ZM<_2U^xZ z%%3%Wh)7t%Gbdx)va+(~88o=u(UYX7EF>;YS6Em`#+6{Lk{L!Q$o;SYuR}SKUz$|f z0hKgc3xdZ%_wi8JbA1^(c_gCnKL7)iV|cf~J{UD1-H_>@tpIBJslOlo15zJsx5CA-uF)Ph zkwil#EnuYEV39gniUD6 zBEBL-J5l~;D}XPlZ?61KT>T1*mdu+$_SH2qx`EZZ9`rYl&dPc%=#VlL%F3z!dT|J3 zq~Z=>S_O@goM50>OF5ZhL>_6e1k$%8jelb%Mn-<`3qvp;TMF!q@tFEsk0~4hH%^fC)U)$d_K(|ZeP(HX%E{UJ z8bYNQx>*mFWmfLZ0=521)_xXjhZ~{)HvlmX<}UI2Bu9Hk2PFa9r{l9R@GT(6X$Obh z#>g&iCE7VCVXW5OrU2l$2S~Aoh>DJeUao7&PKx|-aTEmVEFa%x(6DO$_K!))dOqqM z21eUyIAjR|l3QcU{kXWpXncq|<+O{TPIcVE0MXO)hz+5ekvsj?ty>2R&i9tfkvtTq z7Ek_30&gZ8#3rH=hw&--QA#k9a%=6KO0bd5~V;vb3%UeU_p9Uya$3e1?C^# zBEgSKwH7~vga2r~7?Eic4@ipLRJ2VE2dqf-)h)#kjgB(BM2NHiZ2r>C!0E$gBH>m(xg8ns+ zu%2jcZaxHC7NquHGNX@EQcfe3z`Wmy3nHV1$JQV!tnaOcvzUZpjMA(V!m@1z8$~SM zV?d&gIYSJn+jIutiG)g!qe;kt=Bosy9$`i*Dyqs@am&fMUf)eL(nKXr8y!NXP&2*Z zn;>284_t!}0))uDvV6H@(U3HQt2=~!R~7_UmM45IVcYCSo{b>q6HH!S+6}_zFE@^4 zKrp~#HS{DX3yNJZ`r%5kN>Y>FB~UrkeLG|ilK@|#msV;he6xo*2jZrRVI$a%R8aci z1DCZNuxd>CwesPqOL{iWy^)3Tw19iXGfFGS&;%Esd}be#bL5QR-)wH9*KP@kZAiI0 zPSYNQ{j$B=$7n}|K|1oU(F2K25d7saC&$j=Q4$A4f zBo4}i&@e*tJC`;xs{1P>eOs*JGdJ_#Njf|KOiO98W zf4ayKT($v9ruf;{wB)b_23Me$>=YK(cQwI1#(wY+6m}<@o6h}o!Q@dNV6wn=oc?_h zbQencS^@z)PEtNnfLBV3Le61~D-`DcHn2P6^m*7AWbM*KPZ&CL z6+%s*TGffZD;tkYNH6ERn0RyGu=kDLO1{OU#`+A22zi#uG_P{F<8L=HbC`lb)d=Kk@zN&!br2 zBotU@CnqlCOA!$LcSgIS8Hg-Db_hbRkKrhh8CL{%b=mUj56?B?br~F@jYM2*>@AFCqQtg$c6|C2hjKBpyKwu1{_V^5LI?xJ0e=iE zZlL7&CnMRPgql69WA&J_dq4Oh$S#k**ho5+Uv0sIU2f^Jt z8skAhB%dJ26;=F%tmQ_6Ur>-Iw>AvWbLCo zZ?KWRv$1~i$0u5BrY~G~L54qJbE3C_UH9!^v8V!D@^HgR$lQoNQE%Kx*9d7!8+PV% znAZp2=N*4`u7QlR0>f>Kl1ct}-(>~>0nd8oY>}yh@MWYAppjpNh^5m3#boH`&pyPh zg2KX%AYX_u?!f1gmoNKCJ04_5+-K2ME#-r)4^+Li1T@3|2?qxU;t*`Huqm^bm$m@O zz%5lOu!Ox#5PxSxaIb>yOGs!HBM$E1SaA4&@5<7qh`YCM69jtabSZg0mQVYk7 z(64U*Fjf4D-EwlEh6(B*lJr8Mp`m2-_%i#mCoX1p6ScIUqz`;v)jv6CafQZrbQQ)?RLTBJN|Z#s!h=^K{{TamFW9} zqtb^qPCCd)00m>d<1~V*&c_%rA4omD8h!#fL<<*i_(82s>w)Yy?~kdcpPz$>zE)S zC3=X4hhb%7Akzeukc*3p0G@Wj&=H~oNd4@=ej^`N$EnhGU3$4S^GSsv(M>i)Vge zp$b_GP%1LjOo}pt2hN>8zmY(gi=4~`lcuAmUrUftT&R>5cJCw~AA3iBnZR&nq3Uj+ z74b@3c9i|u)Rc34r!={ zmKNP?`?vQbs|y)paWehu715{o~2O zaWSW?lw4w)Men8mNsU=%ndd|^79MbrW+AvB*)NFpeHsq#t6rt0m(PN zH*-5=^oE+-KaGQJ3q#71wGMU~#`U)3M~*1Xg4*PrZxyHuXMaO~Og72uy9o=gYWTWl zhbCcL(Y0&WLTMvoo#je&id4Eq_AmNvKOx~R@jJSyGgajK-vVSP`bb*R( zl~`&@GIpR1pFmtf^46&D*<16TJ*&Y$iCFo(VsBCqHOwzdYwOg%m)~|+Vo;mB*yOE| z-~YEi3~52q1qFJ91CJ28ac>{_Dz|I6R7d1ItB|EWaCthd^t}oGK8+`;YhxLU%q0;7 zt6JQdaW~2jcp4rw3j+-J1ajei+jd2D-Lx%2S`FU9HK2Pa6jsR2zsUb(9&>4V(M=_M z$5Di=#W9{+nZh$EYi;%0yv7UUl0?bLkC#_9QyrPcc zfr0t6=*bumSO|9`b8>hVFhu)tsgPhEkP{BAF%fLiWOM|!#B2Rya>tb_$CckPI!2WTaQw4U*Mg%!`S`xVUSpCcyI;n%E5l(r^r+>O zrR8;%M~5G^U`ozUJ>`SDO){wo^Kx@vh2#BGhJo9^n3n~&^#kdNbzJZ{5{BW*?L~OQ z+;f1Dax-%!u*n+A@A%`ZQE8s+2gYpSA z1Dq-txvXcnR_IGJ>`#f%urRzfy}Sln^kvj{Z38yMmI%zqTKqhSq=xH64?EmTR79li zOE0vIGa;i57&-qfL@f8&Gs~%Oudq*zO^oOn85lqv6tT3}OntaOJfj`l6zuh)7y?8KklCO44RX-j}K}J2I=-9 zc`Fg(i2;hKU%#pw8-v|@JOgpB;i#+BZ~SS$CG1Lm=kX)X`nGj~r%LbXAtV!a!Ep(O zsVw2xBg-!?uG`SmWB~i(QR(W+pWx)3>gUcqS+f%)-iYCd?iI}Mtk(gcPqiD2F*esl z(ueVg?rli(R)|f%$Op!KRa2wniBA|eHEJc)L9tWby?yu22=l!unhCPU&v)h~t=Wn1 zg9xsusECS*F?<^-e;KkaLyUR~dd)7d-_&p8T&PMK9pB9%(yPoQ3v6Z}h)!S=9u55CP&26Luf2#1!j(2CUQBW`- z&vitXVIZrhM~{41IE`e;xe?|DBQDqqpanoaJRg$_R`3skuV26ZZFDpfV@7JwOLQ^v zGj3wk>O{lq?j98%pZRxm7kP7k|W8re$jOIQV; zxo{yHlNV+*4QeFuuAo*!iObjxh}ze@TXtt*x`* z1!KmB?%mkkjn~$1VhB@tv|mO>81*16>JsM>*jOLn{0M!vq zi^0|$c~=sX_F(@-#f#3)AK2$+W(?t-lQlKSi!Z_|AZ=_P>lPF+K}=R4l=lmE_3tj1 z_YKrv{yc24aZT*~9F;18Q#;g|$BWUFQ|(U;!4Jm@T~O4kQ4d&vPjwcl+;N&2$)`|; zLJ)MYx}_x&v6@_@XUS+cNEM@Y=>v`kmvNDSCN$CnDRtj66a|wX_BxK@H+7R-S+yB9b!vuEKeM zM1e1qMWm$eU-z%D$;89qgXp`ipDXg)T6`QXoF7nKx5tT zH#x&vv@1+fFD);7j${?>_x#02rk3Hh^(M7@AWMRQj9bXHOhS^UXJDuWh4=h3Rup&8 z%{H#~Ez)oxDNi)xs<8(Tz$~Jmpde?GmPRaA*Qgsi`A)Z}mM+hJvDwE8M-&q_XF;bl z;gm&1Mahs0zPv|=qoDV)Tj%RYUJ9JgkOW>jMnq}=v;7b+F#J=1asr^kfMqAY1EILM z*x-+30IlNCTLDiIVP*D@aH-%Lz`=M3k?hxvP3Z3ZXpP_K@a+dpD8tAuJ%ogh?`lx% z<=%*&3Pg3ai-VtJ<-c0{OV@{Uk?bUp`aBWDWWg7Pkl!q#u>(!}>D?ymVI;F|hK04e zGbcxJ_%2;6Yw{Hq75&j}Y+;4X8IABxmQ+yNH>M5#nhzA)%LOn`@9euD%d5mY1kUuSfMF;};xQB!F8+!-`e?})q0g3oo&fa7F;bEM^Zm%9# zb-m^i;QJBjRTDy65HFHOt9W}DUj_x6$g0wh0!=F)5@}%hy`qctb($u7`;yG{yN=I? zJDm}z8oClHNKXR@Vl}&~VNu5;fyja+AbuS1nB)R!A{!${ zSXBQ$oZ=0*aqbI%fAz2xSfuQkG0xgoHrMkAWbPkJv+n4)Kka~oq@)$b-o3FE$^C{D zNd)#j^+!0(i;-e2@CnMo-YZKUva+&gdCj=}J=FeQ zUNjzt3t~wKqP~M)OU}b09#2x#LqA`4@5bwP29x7E_$sVq@CSSckfue_Chl6^!`K3~ zKAwv|QxCY0??4U(ar_$yD15+IfdJz;%FK1Be*x$xYf2Cu?*+CgAW>b(zaJ`%yhMW6 z_yO_>nV2x+>xha+7EH64j_8uHLNs)A_c(ul|3HQ;KuuQiH3m1)^+{6)GRb^J42XVk zNXT(mLL@N8sb*kgl=I&iK$|=s?G9G+v`8asnYzzwSS2(Zv~sQD_gGtI~fVJ!NPa#B+bs{aOl za@iX(OmMg;mDFUhlSt49ndBS39t^@1F6$LyQtufuv#?kc!20h@@uTN-RzEJRE=#tyY-`DPWNwd{E8^KKWlv! z|L_iZ$)TLKzd96oSDu>N)(_#2ZMw`={*v_5?7{OII1nA$X2KJcOv#r%@$vp=!t`G$ z)c^K3qb`E}X>?G!N*FF~+{?DtCy-|)`E6@d^c~(mT^n^@+DDN~ZoAoq&4tz7K67GE zt%byev$yD@9+4z!P7OOnISk*~6MmR}t*6qFE!)a@<701%FK$($x)^cx9atJp3p)tV z|M&;8e7LErYf=kJ#ee-iCm5IKNLd(W-=OtofVVedXhqL`@!e>~1g+aPQT|-J718AD zk96bGhh(4Zz5Zh>&n}+yuKtrb@Vrk6(JQ`7#_f69&AdID*ae~^q& zH&t_MtY$^ra^sG@&&2zeLnd0?RR&bYs9iQ4n0}ii6h7{&y&?X|ct zmgBBVR}ANTsJLx8;>gD_lb!IQF?-=Qu~5YKc(eKy-7nu#shY=`BxP(Yl!r;59BH2zJuy4r9gq{~lO^MRV{9`~x0|!RE}hL$ru^&Dn&4l- zo%xF_?q<^5WX4H^$!pY`?1{|S2>#Ana;S;D4=t8{~x!X$b1f@J+H z_*~Fz=PfsFM_Ibpui?I+%R;kNx$lhE$%VKF)}8X!GM+4+`+l}5yUW^pG0XIHSA}Gl zKR*5P#%O#|^q8Zgs=h^y&1y@hWS-^wYB`aT7whzeTh;5ycQKNy2p+8P;gGhF7G1d% zsiiM{ak_VQ+)IvwQJiNZ@+Does->!wP*ii-P&$E{Mo4be8nKsvA{TmuRlgq zCO%%aw6#+IlH~An-;?O<($*pErUUY+n(Pi&?~J(={{9{>cya7UaHDd-`*oa6_DwsR zc9%|=FdR8Ol9}bo@g=Tf`NShBW&<=p}bj_gQjZ+PzAQ5M1$= zeNcBv$fc_!%q6bEZnW`T)k}^`4@U2Q+Bbb6O{Syr5k<%&CIubYi`L)N!quO=e=cto z?(UVuR^dIpY5SEjzxT?CUbgyH$<-fPq6U=iv@Q>fyZV;M$O+8tEbIBSKR^1}@efuF z4l~t1mhtk%nUGAc^Xz|?=ji7Rxymd?-s(@tri836ZT?03uWmQdKB(KR>WpGz+NyIhd4<4@OG3B6a~hI4>V{{@_ftC`zTotK=;(-#ig^<%Mp^SGTGi^K%pu+azqh zw)h?XP1!d-xKcSshn`hH|FJPGk+$HC0J(!CtCt1blNZ&T1(ma&ALSsWROoix!}5walafTKxZ%_LWgluF?J|CWstGk>)5V zNREKCB1lRjND4!zG?IFhR6sySSd>^U?r;HjA%vfc!NU5G{z<+?eb7UK$ zt)u|6gqNVd1dX$@y&?CDy%%ZenGq>5D)54cdY-x>Wkc^9|2KAk4xE*c*>#44rCr?c%tP{?_RBUtOUT!R|ivK zF#Ee__3;-8_khJ)_MQz}K{Y<+dlo$~Uyw`&-j6Ww8VsitITkgrHBc}zsw}i=l8}-( z|tPmcTdrCDSH#?K#fLGNnqp7pZ_Etdioimo?8pX zHV%q_3tQo`_>{wxfg$({R3hNK0aa3cW1~L?04cROQ@allqOeL+DLJ^gfsPLVrU+sK z%7M~T>r#_`Bzx6aAYik{Ww_iLAs`q&@NE3qt7&Akj!Vg>#m<7*p?IV{GAu6t>3qs> z^Voq{W7R@)w)=Ze_ogzYKO{seD(hZ`=hJkwl@<2Ro?Xp?MnmLD_K_*G`TMB&^jwQP z!3@Xlrn2&iu2vK^h)^rYVWa_ge#=8!OBR|%_+aM%JiZAZ479}00hj=5R!8~U*)s2W z{@ziriOay4tF(_(j6tK{k|+KrYf;&%i$aW0BeECBLRlG z$J*6(R6PnH83JT~^P0>!a3`QWj)&3AnPci2I^`${mx#s0ZIj_0ZO4PY6fz}8iaA=( zzcHW?ZMAFLqI=PnuAckV#v~AapkG-&^2G3?ku)^1@>k@Uk0a!k(siWV-+b7#x;l7^ zi(vafJ^!MJ;U++Rgv1V*C1{dX|J^I3=H%Vn1VOC~j6Pw$DjDs#(S_AGozI~$hUvf^Kf&p3UkE0k-Qotd81<{(BE5ZoHAz%)U78IAgF~ z>Z3P9I8x!tEzgEQvqzW6Yt zD0V;iH&wP-Q)E^@H0>}p#IM?ov&)cBuql}W;#*cRQe)5}{y|8Su>D5?OWeek;$7c& zvKJ2#t0IzpaW^unf(#}(XkD*_2Q|^tbeBCuLKSaqCSw~oY%{Mpp0NS)sSdX@g9YKq zfxX_JyLDPOKl$cr9*>$yvIQSR)~KeocYgn{Cg@ZRhFf6MDHJ zH@mpQbyI>xq`LY@ymW8%_cWm#qf{ZUb_8D)C*drDZ$eEby?3A6@syNbl8(x;&D8E##;`- zfA#*Vt$nGC6yclgp{r0@s9!3z=-vsgft(?&3Nk_s56_#*GIitWZ}Z|BVcO_u<**TA zVf9au*bRQcupw8SteY(&R5)S^15B$OU(6=$ycXr#G6?3_V>>#(IUCs_VLW&K;R~y7 z#U6(zMx(~TvO$CTZME*_`SCGU`tQnvGGo}l>8O{!pa2h%hw0hYOMkB}-iMAnw`AC^ zxYY6a-UI$fjuc+|l;Wh?!qd$P?KxPThU&LLL7xl)-Sk%*13_JK!;x@CK$+*Od9*Ud zAtc3cg1s>EeL{50;qQ%slg8uo)|EMfU8S<0HKrz}>uCc=K1Db%5r{AV89DNpptG2W z5OD3&^i-aII*nKUGYT)4E3a5p=ZaA}I)DgnZE-qqP`+(*G-Glp8y!e(YA^N{lL=OO1|s*@!T@N#)$HcUCM0Z8HgUyaMeFH6eqx>c3Fh5`B7H03z1 zDeekmcx_`#y80H`Leyo0kCuwN^RH9xecz{oewgq(oA)sVp{w-chuZ@V%reZzL!Hrt zPEx#IPuq8^N6W2kR0cP2k1#Ng{5VQp=^kVJ!7k~uOM&*;MhgoThqq~v5GnZj(t;K%cMz7oj z$r)hk1kbMJT>|w8!oF@`-AIgZ%PT7#zz)Bd?4*s=`Tb6YVy9bvH*4jJ`YVZsqJ}wR!s7=|P8pFMh2%Tfn>7_8IJ8cpUmG!`wUlvhdZXAz!w#Zf#+@pm z?mq|kv$C(>he%o#en-5tQohoFm=D5+HE%qW*kNn}U;kf11PAA9c4lhYdqk8elyYtipW#5fS{FrL<}#*Uj+bV5Yr68G zx*-6Q`a5flW~%Yii`Tp;{^^dNlEOvDXEFs-Ci0mau523oh0$ijsG9v;P5)sfTEg{w z=d!kwrjr$Ig7Vt_tGC;+g;kgNxaD_DmknBy$E#-x9~kP0=%_;##U0_w zshx|lTMlap6M@dO7OnVJE5uha;oHK{xn)S})i3*3QsX6QytKXGSJX8w_Y{Kb>v+gd zul{Ide7@oOWyhwP55=F3@5H*ll?c*EPuE=!!s8j=sC}b*A^3@{pO>id&<`rSq;5y_}N?>w_`jnh_#N1@w^zOt{Y}q$Gcu7)}6|gmrWpqcE znRkqW4}HRcU}kr$auxM1YYMZs6mj^u#KU9e2czfs;IW`I)CtlcW&5q9^AI)+L&;Uo z^nz;pb&*5EHDuSc3_SAB`VI|twtNamLI;IXpT?Nm0(FaX0vUr;k51o@f=C}kLcj=_ zD~877?lZ?8li|_POWqXhM~xm+XxUw#z7?uCDy@oRE)I>TWoM)Y@{>#2RFglgDVs%c zTFZx_1?Izu`-giT6CIdtE879n_VTtPl?HOd8?7sqeiqwB17=~`*GON6z9qva+sm$2 zJWKZ2xCqVf#fuj{ch6yWVZOOT&-GNoRCw5ES$QX>vK#sB_-cG0D4{OA1SjBXo6smh zJqH;#1j#dKB0qh$czAb<@sMxsXy1$N)F*uBcLD-AX%j}=Y30HOKqmrrMS~G%T$}{&2mE2S7FR!H({J zVRyDyMrR4PTSwBI>}B759>9yCK-TBH(u%u$HKOij~+m~D{)NrWN0)b2b5)j9+nDZ}=D`%+>ru!=mE7{RmO|BvFJGSi+cCx>?A_=1GCaRfjx;8DQgara78fuHuTlZ6_;aUVd1hTRkZdLp{(rWJE7tuZ~~`R!ML zUATDFBrJQ#6ydNo);vSM>66aC7*)tsfTAe`#;pTG#fu z6~(a-t2w2%cx|Apf7Jq;G9CRd%vIEyr)act?do6u$fgX9ew06~1P;cpXMSVv-BDtMYMjTKl7xy844YD5GRH3(4lTT%o~gV*AYdfG3##J~j$lO0L->ziV;Y7fm1Q z_dSwEyCQr2kfm+>!{T*X5A5k-W5{iDt8cZc50TbE7*{dKbSkB%L-GSjkLlHaQz zqRV9!hcvOOPxkj>=jOuE>{pFJGe*gQlhQ}rPJcClgivDmD} zOYSdyw6&!jc(UM&lF=QWYwZPlwcoV&wq{H?Fi^R1p2S;|$aL5JE#0EgA=g-EwI~+e7&7*1UFLSwU2H*|h z+#DG8!C6z}GOrHV4X0|s&B5NvGu0Y~@C+^j=DR|cf6_MdyB$1mrFu+1I6GKW{) zh%7f0MZw<4QsI0&LLG8BjCER<#2^{3ZXdX~G7FKKF$btQ0n`nEbYMMXv|hj^9Tsb( z@v9a2u6u@%Dh{1^WzN-pBPPH*DRC-2YwT|AT$R+TNd*R)sttuH#~}*&^($JEBf>|Q z!rwYCQTJX{ zw94X-qFxM8U6_}p=Q}Mt8GSvD&MW7M)^*A}blh;QL>^7Qj-hyQ=jux`4Z-)mOi9I+ z?N_~6DPOjA3xaQ2{LpagM?Wr>_(bA9fIwtoG`v7b#fy(zVoyr61KtMeR{M!J12jiv zuI@~W=9I(9D~~V{^qf^4Dn}B~vtjoTv?r*D&0Uuek`PseYsz+FjoG1@bNja1O?POW z`*e;ApusJ*+dIX(^xD{z#bWJ2COPk2{sve)5)?kmE3}#qhdrmPt*n|s!A}LsPsjpx z$1S!4BnJkp^_CAtme6~_UO+A|qL=jM3uicC!Cgugik(QE;7@u}gV@IcTSLT`iDdu9 zgLQrBhDIj^}A2dsk20H#ada9}2=P#IiU3vZIgm+qcNf0Yl47aquMVt3=G-i~U(?{Pcuy^c^TvL8kUuJ)5K8 z0%#Dh-?S8UHy!HA3Nc#FX{DUExs9O@hti+HUU&iS@V)QPnGZ3~E5t|1kO-vUvCWrd zZwnO*bNaZmwc{srHTlHOHcTtH68#$b1Fq(-=1WHWbWy<}Q62erN?~1ftS_>mKU|A= zBjZCV61E_B^7M#Loi=Lz(u4V-tL@8TEikrJAekg+9(a0-V4g7kXle{N< zL(!H1T&gk2Icd9IOLr;ZS+``KL{s~X;(MB=xFH$=r5hR-H{jEI0vcMXdzOFuWb7D^ zc@gK{6H$9gKFF7y-&Y{l5OJY~c85Na?Xfv1w%kSRBE_OX{v$F0bC$Qzu_z4HYBoKs zB4Uc8H^p5bUXxt&1o9hAdw*#uCEksIix|Mg5D=m3t0i%#RD_jAwC$lv!R8D6uM0zz zO*2*r)O@yQjJ($x4LYJybO2<11jy+0a^aTB$m{C3uiJHxxO!;>U=eRW##8SN$Soxd zN~(H==iD&#I0Z`mWWTi+6CDj3xSUyzYF$knqmP@CwP&WB-X}9mr6^$<2kqM!Aa!+} ztIM1fEYK1MSx@!juAFF|1Ulsd7PVnK6-Yj<>pBeKM;A6OO~$EptjZMfP*r%a)62Sh z&u!!~T;E@c?U_Gz-ue08390Mvfev-~=PcDFe(`8{?XTn(?~LLX0K+FM|dC3W9TG36Eo=J z*LTw}Ycxy#gZlX^tI}MbKeEH5JJnD@0i*G`%yY_B5|gfmzI$e1g=9#x`j@&wcpvZ0 zLKeWH=q0r<2&bA8eM7?ui4GkSC+|REW1PjG);1>S!q80(tbKL0s$q|9vs!cF<$I6k zW>nQD)aN?0i4hrX1nX+l$n#2qI1R8pat;<2e^A*JnGe%QsRWwLngsT#^5{wq(DW4) zn2i>5FbEUg0klLM?Ad3>$}@O8;>BR`8YJ{{8`8%dx+k!f91TYkW9y zxyz@32qb{nF~Fq|!0ZC?%6kB2hop{8%>R>+LR(;iGq~EC-8R8eV_qi$cg@bSL$FJ=G4iC^iQM6r4alRfnpG0|bL9jCpLC1$WWqN4E%P5x3Ks!+m0 z7-x26Zw1Ft$R9FAOtm!Qb|=sI@A=2tw63jhr)mT$|8H4Vj#h5`u7b$Fq-jw60=S02 zogM&^TxSSa^F^R zuG4stHDxq;)_ggyt*@yBtXW)eoGG*E`?$@%6!?zS{6o^m$Ah7F`Hx#WdmU}Zg5W3; z`vQ9i+QdRb4MIK;m)_4W=;e$j>}^0Elfa+?eo(NigZ~Ob3sm&==`IFe)DN$0Pm%Zg zkoZwQBdjQxk4BFSu+iA33C1SKf8tQoGv8-`#rw>!W^8Km1_;VWO^>qLdb0$qp*42l z^a<^Gl%>>Ca7HU3mrpZlc$rB^ytH*xF+_p==WU`-?C7A>!&?CD0H$-hl`HMj*?K$6kEj{Xb86_yfm#>bme8U4L$(w@BT%_GrD}NvYpbVVx&c1WG{Zt- zQ@bn9sui+G>gutlL(9*>0V7Lxc;Imx+uJY03y`_G`nkKfKvR7dQgZ`oEXWA3CR{a+ zOmMNlF#@3MzRk`~h-oLJnL?sfOJYJq!~-zK$00!m3r@}XV|x$@LP`#iHHrp&%K#Uu z=Js0N<+oUs_`}m(Pz$upx!xiH8v#^?=|77Ee)xaBIh1t5B +

+ + + + + + + + + + + +
values
Insertion order
values[0]
values[1]
...
object
weakrefs
dict pointer
GC info 0
GC info 1
refcount
__class__
values flags
values[0]
values[1]
...
Insertion order
> + + ] + + class [ + shape = none + label = < + + + + + +
class
...
dict_offset
...
cached_keys
> + ] + + keys [label = "dictionary keys"; fillcolor="lightgreen"; style="filled"] + NULL [ label = " NULL"; shape="plain"] + object:w -> NULL + object:h -> class:head + object:dv -> NULL + class:k -> keys + + oop [ label = "pointer"; shape="plain"] + oop -> object:r +} diff --git a/Objects/object_layout_313.png b/Objects/object_layout_313.png new file mode 100644 index 0000000000000000000000000000000000000000..f7059c286c84e6cd9a71f59b640a628a110d80ac GIT binary patch literal 35055 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 + object + weakrefs + dict pointer + GC info 0 + GC info 1 + refcount + __class__ + opaque (extension) data + ... + __slot__ 0 + ... + > + ] + + oop [ label = "pointer"; shape="plain"] + oop -> object:r +} diff --git a/Objects/object_layout_full_313.png b/Objects/object_layout_full_313.png new file mode 100644 index 0000000000000000000000000000000000000000..7352ec69e5d8db2c59135cf39aa0e6f7dd323cc9 GIT binary patch literal 16983 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 T4t`JsK`0(ok$ZXg{Ehz)XB)p= literal 0 HcmV?d00001 diff --git a/Objects/typeobject.c b/Objects/typeobject.c index 2ef79fbf17b..6df9986b82e 100644 --- a/Objects/typeobject.c +++ b/Objects/typeobject.c @@ -1861,7 +1861,7 @@ type_call(PyObject *self, PyObject *args, PyObject *kwds) PyObject * _PyType_NewManagedObject(PyTypeObject *type) { - assert(type->tp_flags & Py_TPFLAGS_MANAGED_DICT); + assert(type->tp_flags & Py_TPFLAGS_INLINE_VALUES); assert(_PyType_IS_GC(type)); assert(type->tp_new == PyBaseObject_Type.tp_new); assert(type->tp_alloc == PyType_GenericAlloc); @@ -1870,11 +1870,6 @@ _PyType_NewManagedObject(PyTypeObject *type) if (obj == NULL) { return PyErr_NoMemory(); } - _PyObject_DictOrValuesPointer(obj)->dict = NULL; - if (_PyObject_InitInlineValues(obj, type)) { - Py_DECREF(obj); - return NULL; - } return obj; } @@ -1888,9 +1883,13 @@ _PyType_AllocNoTrack(PyTypeObject *type, Py_ssize_t nitems) * flag to indicate when that is safe) it does not seem worth the memory * savings. An example type that doesn't need the +1 is a subclass of * tuple. See GH-100659 and GH-81381. */ - const size_t size = _PyObject_VAR_SIZE(type, nitems+1); + size_t size = _PyObject_VAR_SIZE(type, nitems+1); const size_t presize = _PyType_PreHeaderSize(type); + if (type->tp_flags & Py_TPFLAGS_INLINE_VALUES) { + assert(type->tp_itemsize == 0); + size += _PyInlineValuesSize(type); + } char *alloc = _PyObject_MallocWithType(type, size + presize); if (alloc == NULL) { return PyErr_NoMemory(); @@ -1911,6 +1910,9 @@ _PyType_AllocNoTrack(PyTypeObject *type, Py_ssize_t nitems) else { _PyObject_InitVar((PyVarObject *)obj, type, nitems); } + if (type->tp_flags & Py_TPFLAGS_INLINE_VALUES) { + _PyObject_InitInlineValues(obj, type); + } return obj; } @@ -2060,6 +2062,10 @@ subtype_clear(PyObject *self) if ((base->tp_flags & Py_TPFLAGS_MANAGED_DICT) == 0) { PyObject_ClearManagedDict(self); } + else { + assert((base->tp_flags & Py_TPFLAGS_INLINE_VALUES) == + (type->tp_flags & Py_TPFLAGS_INLINE_VALUES)); + } } else if (type->tp_dictoffset != base->tp_dictoffset) { PyObject **dictptr = _PyObject_ComputedDictPointer(self); @@ -2210,14 +2216,7 @@ subtype_dealloc(PyObject *self) /* If we added a dict, DECREF it, or free inline values. */ if (type->tp_flags & Py_TPFLAGS_MANAGED_DICT) { - PyDictOrValues *dorv_ptr = _PyObject_DictOrValuesPointer(self); - if (_PyDictOrValues_IsValues(*dorv_ptr)) { - _PyObject_FreeInstanceAttributes(self); - } - else { - Py_XDECREF(_PyDictOrValues_GetDict(*dorv_ptr)); - } - dorv_ptr->values = NULL; + PyObject_ClearManagedDict(self); } else if (type->tp_dictoffset && !base->tp_dictoffset) { PyObject **dictptr = _PyObject_ComputedDictPointer(self); @@ -3161,19 +3160,26 @@ subtype_setdict(PyObject *obj, PyObject *value, void *context) return func(descr, obj, value); } /* Almost like PyObject_GenericSetDict, but allow __dict__ to be deleted. */ - dictptr = _PyObject_GetDictPtr(obj); - if (dictptr == NULL) { - PyErr_SetString(PyExc_AttributeError, - "This object has no __dict__"); - return -1; - } if (value != NULL && !PyDict_Check(value)) { PyErr_Format(PyExc_TypeError, "__dict__ must be set to a dictionary, " "not a '%.200s'", Py_TYPE(value)->tp_name); return -1; } - Py_XSETREF(*dictptr, Py_XNewRef(value)); + if (Py_TYPE(obj)->tp_flags & Py_TPFLAGS_MANAGED_DICT) { + PyObject_ClearManagedDict(obj); + _PyObject_ManagedDictPointer(obj)->dict = (PyDictObject *)Py_XNewRef(value); + } + else { + dictptr = _PyObject_ComputedDictPointer(obj); + if (dictptr == NULL) { + PyErr_SetString(PyExc_AttributeError, + "This object has no __dict__"); + return -1; + } + Py_CLEAR(*dictptr); + *dictptr = Py_XNewRef(value); + } return 0; } @@ -5849,10 +5855,6 @@ object_new(PyTypeObject *type, PyObject *args, PyObject *kwds) if (obj == NULL) { return NULL; } - if (_PyObject_InitializeDict(obj)) { - Py_DECREF(obj); - return NULL; - } return obj; } @@ -6036,6 +6038,11 @@ compatible_for_assignment(PyTypeObject* oldto, PyTypeObject* newto, const char* !same_slots_added(newbase, oldbase))) { goto differs; } + if ((oldto->tp_flags & Py_TPFLAGS_INLINE_VALUES) != + ((newto->tp_flags & Py_TPFLAGS_INLINE_VALUES))) + { + goto differs; + } /* The above does not check for the preheader */ if ((oldto->tp_flags & Py_TPFLAGS_PREHEADER) == ((newto->tp_flags & Py_TPFLAGS_PREHEADER))) @@ -6137,14 +6144,18 @@ object_set_class(PyObject *self, PyObject *value, void *closure) if (compatible_for_assignment(oldto, newto, "__class__")) { /* Changing the class will change the implicit dict keys, * so we must materialize the dictionary first. */ - assert((oldto->tp_flags & Py_TPFLAGS_PREHEADER) == (newto->tp_flags & Py_TPFLAGS_PREHEADER)); - _PyObject_GetDictPtr(self); - if (oldto->tp_flags & Py_TPFLAGS_MANAGED_DICT && - _PyDictOrValues_IsValues(*_PyObject_DictOrValuesPointer(self))) - { - /* Was unable to convert to dict */ - PyErr_NoMemory(); - return -1; + if (oldto->tp_flags & Py_TPFLAGS_INLINE_VALUES) { + PyDictObject *dict = _PyObject_ManagedDictPointer(self)->dict; + if (dict == NULL) { + dict = (PyDictObject *)_PyObject_MakeDictFromInstanceAttributes(self); + if (dict == NULL) { + return -1; + } + _PyObject_ManagedDictPointer(self)->dict = dict; + } + if (_PyDict_DetachFromObject(dict, self)) { + return -1; + } } if (newto->tp_flags & Py_TPFLAGS_HEAPTYPE) { Py_INCREF(newto); @@ -7774,6 +7785,9 @@ type_ready_managed_dict(PyTypeObject *type) return -1; } } + if (type->tp_itemsize == 0 && type->tp_basicsize == sizeof(PyObject)) { + type->tp_flags |= Py_TPFLAGS_INLINE_VALUES; + } return 0; } @@ -7901,6 +7915,8 @@ PyType_Ready(PyTypeObject *type) /* Historically, all static types were immutable. See bpo-43908 */ if (!(type->tp_flags & Py_TPFLAGS_HEAPTYPE)) { type->tp_flags |= Py_TPFLAGS_IMMUTABLETYPE; + /* Static types must be immortal */ + _Py_SetImmortalUntracked((PyObject *)type); } int res; diff --git a/Python/bytecodes.c b/Python/bytecodes.c index bfb378c4a41..ce208aac9c7 100644 --- a/Python/bytecodes.c +++ b/Python/bytecodes.c @@ -1897,14 +1897,12 @@ dummy_func( op(_CHECK_MANAGED_OBJECT_HAS_VALUES, (owner -- owner)) { assert(Py_TYPE(owner)->tp_dictoffset < 0); - assert(Py_TYPE(owner)->tp_flags & Py_TPFLAGS_MANAGED_DICT); - PyDictOrValues *dorv = _PyObject_DictOrValuesPointer(owner); - DEOPT_IF(!_PyDictOrValues_IsValues(*dorv) && !_PyObject_MakeInstanceAttributesFromDict(owner, dorv)); + assert(Py_TYPE(owner)->tp_flags & Py_TPFLAGS_INLINE_VALUES); + DEOPT_IF(!_PyObject_InlineValues(owner)->valid); } split op(_LOAD_ATTR_INSTANCE_VALUE, (index/1, owner -- attr, null if (oparg & 1))) { - PyDictOrValues dorv = *_PyObject_DictOrValuesPointer(owner); - attr = _PyDictOrValues_GetValues(dorv)->values[index]; + attr = _PyObject_InlineValues(owner)->values[index]; DEOPT_IF(attr == NULL); STAT_INC(LOAD_ATTR, hit); Py_INCREF(attr); @@ -1947,16 +1945,15 @@ dummy_func( op(_CHECK_ATTR_WITH_HINT, (owner -- owner)) { assert(Py_TYPE(owner)->tp_flags & Py_TPFLAGS_MANAGED_DICT); - PyDictOrValues dorv = *_PyObject_DictOrValuesPointer(owner); - DEOPT_IF(_PyDictOrValues_IsValues(dorv)); - PyDictObject *dict = (PyDictObject *)_PyDictOrValues_GetDict(dorv); + PyManagedDictPointer *managed_dict = _PyObject_ManagedDictPointer(owner); + PyDictObject *dict = managed_dict->dict; DEOPT_IF(dict == NULL); assert(PyDict_CheckExact((PyObject *)dict)); } op(_LOAD_ATTR_WITH_HINT, (hint/1, owner -- attr, null if (oparg & 1))) { - PyDictOrValues dorv = *_PyObject_DictOrValuesPointer(owner); - PyDictObject *dict = (PyDictObject *)_PyDictOrValues_GetDict(dorv); + PyManagedDictPointer *managed_dict = _PyObject_ManagedDictPointer(owner); + PyDictObject *dict = managed_dict->dict; DEOPT_IF(hint >= (size_t)dict->ma_keys->dk_nentries); PyObject *name = GETITEM(FRAME_CO_NAMES, oparg>>1); if (DK_IS_UNICODE(dict->ma_keys)) { @@ -2070,16 +2067,17 @@ dummy_func( DISPATCH_INLINED(new_frame); } - op(_GUARD_DORV_VALUES, (owner -- owner)) { - assert(Py_TYPE(owner)->tp_flags & Py_TPFLAGS_MANAGED_DICT); - PyDictOrValues dorv = *_PyObject_DictOrValuesPointer(owner); - DEOPT_IF(!_PyDictOrValues_IsValues(dorv)); + op(_GUARD_DORV_NO_DICT, (owner -- owner)) { + assert(Py_TYPE(owner)->tp_dictoffset < 0); + assert(Py_TYPE(owner)->tp_flags & Py_TPFLAGS_INLINE_VALUES); + DEOPT_IF(_PyObject_ManagedDictPointer(owner)->dict); + DEOPT_IF(_PyObject_InlineValues(owner)->valid == 0); } op(_STORE_ATTR_INSTANCE_VALUE, (index/1, value, owner --)) { - PyDictOrValues dorv = *_PyObject_DictOrValuesPointer(owner); STAT_INC(STORE_ATTR, hit); - PyDictValues *values = _PyDictOrValues_GetValues(dorv); + assert(_PyObject_ManagedDictPointer(owner)->dict == NULL); + PyDictValues *values = _PyObject_InlineValues(owner); PyObject *old_value = values->values[index]; values->values[index] = value; if (old_value == NULL) { @@ -2094,7 +2092,7 @@ dummy_func( macro(STORE_ATTR_INSTANCE_VALUE) = unused/1 + _GUARD_TYPE_VERSION + - _GUARD_DORV_VALUES + + _GUARD_DORV_NO_DICT + _STORE_ATTR_INSTANCE_VALUE; inst(STORE_ATTR_WITH_HINT, (unused/1, type_version/2, hint/1, value, owner --)) { @@ -2102,9 +2100,8 @@ dummy_func( assert(type_version != 0); DEOPT_IF(tp->tp_version_tag != type_version); assert(tp->tp_flags & Py_TPFLAGS_MANAGED_DICT); - PyDictOrValues dorv = *_PyObject_DictOrValuesPointer(owner); - DEOPT_IF(_PyDictOrValues_IsValues(dorv)); - PyDictObject *dict = (PyDictObject *)_PyDictOrValues_GetDict(dorv); + PyManagedDictPointer *managed_dict = _PyObject_ManagedDictPointer(owner); + PyDictObject *dict = managed_dict->dict; DEOPT_IF(dict == NULL); assert(PyDict_CheckExact((PyObject *)dict)); PyObject *name = GETITEM(FRAME_CO_NAMES, oparg); @@ -2898,9 +2895,8 @@ dummy_func( } op(_GUARD_DORV_VALUES_INST_ATTR_FROM_DICT, (owner -- owner)) { - assert(Py_TYPE(owner)->tp_flags & Py_TPFLAGS_MANAGED_DICT); - PyDictOrValues *dorv = _PyObject_DictOrValuesPointer(owner); - DEOPT_IF(!_PyDictOrValues_IsValues(*dorv) && !_PyObject_MakeInstanceAttributesFromDict(owner, dorv)); + assert(Py_TYPE(owner)->tp_flags & Py_TPFLAGS_INLINE_VALUES); + DEOPT_IF(!_PyObject_InlineValues(owner)->valid); } op(_GUARD_KEYS_VERSION, (keys_version/2, owner -- owner)) { @@ -2972,10 +2968,9 @@ dummy_func( unused/2 + _LOAD_ATTR_NONDESCRIPTOR_NO_DICT; - op(_CHECK_ATTR_METHOD_LAZY_DICT, (owner -- owner)) { - Py_ssize_t dictoffset = Py_TYPE(owner)->tp_dictoffset; - assert(dictoffset > 0); - PyObject *dict = *(PyObject **)((char *)owner + dictoffset); + op(_CHECK_ATTR_METHOD_LAZY_DICT, (dictoffset/1, owner -- owner)) { + char *ptr = ((char *)owner) + MANAGED_DICT_OFFSET + dictoffset; + PyObject *dict = *(PyObject **)ptr; /* This object has a __dict__, just not yet created */ DEOPT_IF(dict != NULL); } @@ -2993,7 +2988,7 @@ dummy_func( unused/1 + _GUARD_TYPE_VERSION + _CHECK_ATTR_METHOD_LAZY_DICT + - unused/2 + + unused/1 + _LOAD_ATTR_METHOD_LAZY_DICT; inst(INSTRUMENTED_CALL, (unused/3 -- )) { @@ -3294,6 +3289,7 @@ dummy_func( DEOPT_IF(!PyType_Check(callable)); PyTypeObject *tp = (PyTypeObject *)callable; DEOPT_IF(tp->tp_version_tag != read_u32(cache->func_version)); + assert(tp->tp_flags & Py_TPFLAGS_INLINE_VALUES); PyHeapTypeObject *cls = (PyHeapTypeObject *)callable; PyFunctionObject *init = (PyFunctionObject *)cls->_spec_cache.init; PyCodeObject *code = (PyCodeObject *)init->func_code; diff --git a/Python/executor_cases.c.h b/Python/executor_cases.c.h index ce0dc235c54..82f2171f1ed 100644 --- a/Python/executor_cases.c.h +++ b/Python/executor_cases.c.h @@ -1753,9 +1753,8 @@ PyObject *owner; owner = stack_pointer[-1]; assert(Py_TYPE(owner)->tp_dictoffset < 0); - assert(Py_TYPE(owner)->tp_flags & Py_TPFLAGS_MANAGED_DICT); - PyDictOrValues *dorv = _PyObject_DictOrValuesPointer(owner); - if (!_PyDictOrValues_IsValues(*dorv) && !_PyObject_MakeInstanceAttributesFromDict(owner, dorv)) JUMP_TO_JUMP_TARGET(); + assert(Py_TYPE(owner)->tp_flags & Py_TPFLAGS_INLINE_VALUES); + if (!_PyObject_InlineValues(owner)->valid) JUMP_TO_JUMP_TARGET(); break; } @@ -1766,8 +1765,7 @@ (void)null; owner = stack_pointer[-1]; uint16_t index = (uint16_t)CURRENT_OPERAND(); - PyDictOrValues dorv = *_PyObject_DictOrValuesPointer(owner); - attr = _PyDictOrValues_GetValues(dorv)->values[index]; + attr = _PyObject_InlineValues(owner)->values[index]; if (attr == NULL) JUMP_TO_JUMP_TARGET(); STAT_INC(LOAD_ATTR, hit); Py_INCREF(attr); @@ -1784,8 +1782,7 @@ (void)null; owner = stack_pointer[-1]; uint16_t index = (uint16_t)CURRENT_OPERAND(); - PyDictOrValues dorv = *_PyObject_DictOrValuesPointer(owner); - attr = _PyDictOrValues_GetValues(dorv)->values[index]; + attr = _PyObject_InlineValues(owner)->values[index]; if (attr == NULL) JUMP_TO_JUMP_TARGET(); STAT_INC(LOAD_ATTR, hit); Py_INCREF(attr); @@ -1837,9 +1834,8 @@ PyObject *owner; owner = stack_pointer[-1]; assert(Py_TYPE(owner)->tp_flags & Py_TPFLAGS_MANAGED_DICT); - PyDictOrValues dorv = *_PyObject_DictOrValuesPointer(owner); - if (_PyDictOrValues_IsValues(dorv)) JUMP_TO_JUMP_TARGET(); - PyDictObject *dict = (PyDictObject *)_PyDictOrValues_GetDict(dorv); + PyManagedDictPointer *managed_dict = _PyObject_ManagedDictPointer(owner); + PyDictObject *dict = managed_dict->dict; if (dict == NULL) JUMP_TO_JUMP_TARGET(); assert(PyDict_CheckExact((PyObject *)dict)); break; @@ -1852,8 +1848,8 @@ oparg = CURRENT_OPARG(); owner = stack_pointer[-1]; uint16_t hint = (uint16_t)CURRENT_OPERAND(); - PyDictOrValues dorv = *_PyObject_DictOrValuesPointer(owner); - PyDictObject *dict = (PyDictObject *)_PyDictOrValues_GetDict(dorv); + PyManagedDictPointer *managed_dict = _PyObject_ManagedDictPointer(owner); + PyDictObject *dict = managed_dict->dict; if (hint >= (size_t)dict->ma_keys->dk_nentries) JUMP_TO_JUMP_TARGET(); PyObject *name = GETITEM(FRAME_CO_NAMES, oparg>>1); if (DK_IS_UNICODE(dict->ma_keys)) { @@ -1967,12 +1963,13 @@ /* _LOAD_ATTR_GETATTRIBUTE_OVERRIDDEN is not a viable micro-op for tier 2 because it uses the 'this_instr' variable */ - case _GUARD_DORV_VALUES: { + case _GUARD_DORV_NO_DICT: { PyObject *owner; owner = stack_pointer[-1]; - assert(Py_TYPE(owner)->tp_flags & Py_TPFLAGS_MANAGED_DICT); - PyDictOrValues dorv = *_PyObject_DictOrValuesPointer(owner); - if (!_PyDictOrValues_IsValues(dorv)) JUMP_TO_JUMP_TARGET(); + assert(Py_TYPE(owner)->tp_dictoffset < 0); + assert(Py_TYPE(owner)->tp_flags & Py_TPFLAGS_INLINE_VALUES); + if (_PyObject_ManagedDictPointer(owner)->dict) JUMP_TO_JUMP_TARGET(); + if (_PyObject_InlineValues(owner)->valid == 0) JUMP_TO_JUMP_TARGET(); break; } @@ -1982,9 +1979,9 @@ owner = stack_pointer[-1]; value = stack_pointer[-2]; uint16_t index = (uint16_t)CURRENT_OPERAND(); - PyDictOrValues dorv = *_PyObject_DictOrValuesPointer(owner); STAT_INC(STORE_ATTR, hit); - PyDictValues *values = _PyDictOrValues_GetValues(dorv); + assert(_PyObject_ManagedDictPointer(owner)->dict == NULL); + PyDictValues *values = _PyObject_InlineValues(owner); PyObject *old_value = values->values[index]; values->values[index] = value; if (old_value == NULL) { @@ -2568,9 +2565,8 @@ case _GUARD_DORV_VALUES_INST_ATTR_FROM_DICT: { PyObject *owner; owner = stack_pointer[-1]; - assert(Py_TYPE(owner)->tp_flags & Py_TPFLAGS_MANAGED_DICT); - PyDictOrValues *dorv = _PyObject_DictOrValuesPointer(owner); - if (!_PyDictOrValues_IsValues(*dorv) && !_PyObject_MakeInstanceAttributesFromDict(owner, dorv)) JUMP_TO_JUMP_TARGET(); + assert(Py_TYPE(owner)->tp_flags & Py_TPFLAGS_INLINE_VALUES); + if (!_PyObject_InlineValues(owner)->valid) JUMP_TO_JUMP_TARGET(); break; } @@ -2658,9 +2654,9 @@ case _CHECK_ATTR_METHOD_LAZY_DICT: { PyObject *owner; owner = stack_pointer[-1]; - Py_ssize_t dictoffset = Py_TYPE(owner)->tp_dictoffset; - assert(dictoffset > 0); - PyObject *dict = *(PyObject **)((char *)owner + dictoffset); + uint16_t dictoffset = (uint16_t)CURRENT_OPERAND(); + char *ptr = ((char *)owner) + MANAGED_DICT_OFFSET + dictoffset; + PyObject *dict = *(PyObject **)ptr; /* This object has a __dict__, just not yet created */ if (dict != NULL) JUMP_TO_JUMP_TARGET(); break; diff --git a/Python/gc.c b/Python/gc.c index a37c1b144e5..a48738835ff 100644 --- a/Python/gc.c +++ b/Python/gc.c @@ -2031,11 +2031,16 @@ gc_alloc(PyTypeObject *tp, size_t basicsize, size_t presize) return op; } + PyObject * _PyObject_GC_New(PyTypeObject *tp) { size_t presize = _PyType_PreHeaderSize(tp); - PyObject *op = gc_alloc(tp, _PyObject_SIZE(tp), presize); + size_t size = _PyObject_SIZE(tp); + if (_PyType_HasFeature(tp, Py_TPFLAGS_INLINE_VALUES)) { + size += _PyInlineValuesSize(tp); + } + PyObject *op = gc_alloc(tp, size, presize); if (op == NULL) { return NULL; } diff --git a/Python/gc_free_threading.c b/Python/gc_free_threading.c index 4524382e4f6..7e4137a8e34 100644 --- a/Python/gc_free_threading.c +++ b/Python/gc_free_threading.c @@ -1639,7 +1639,11 @@ PyObject * _PyObject_GC_New(PyTypeObject *tp) { size_t presize = _PyType_PreHeaderSize(tp); - PyObject *op = gc_alloc(tp, _PyObject_SIZE(tp), presize); + size_t size = _PyObject_SIZE(tp); + if (_PyType_HasFeature(tp, Py_TPFLAGS_INLINE_VALUES)) { + size += _PyInlineValuesSize(tp); + } + PyObject *op = gc_alloc(tp, size, presize); if (op == NULL) { return NULL; } diff --git a/Python/generated_cases.c.h b/Python/generated_cases.c.h index e8e2397b11c..6ee794a05b5 100644 --- a/Python/generated_cases.c.h +++ b/Python/generated_cases.c.h @@ -870,6 +870,7 @@ DEOPT_IF(!PyType_Check(callable), CALL); PyTypeObject *tp = (PyTypeObject *)callable; DEOPT_IF(tp->tp_version_tag != read_u32(cache->func_version), CALL); + assert(tp->tp_flags & Py_TPFLAGS_INLINE_VALUES); PyHeapTypeObject *cls = (PyHeapTypeObject *)callable; PyFunctionObject *init = (PyFunctionObject *)cls->_spec_cache.init; PyCodeObject *code = (PyCodeObject *)init->func_code; @@ -3680,15 +3681,13 @@ // _CHECK_MANAGED_OBJECT_HAS_VALUES { assert(Py_TYPE(owner)->tp_dictoffset < 0); - assert(Py_TYPE(owner)->tp_flags & Py_TPFLAGS_MANAGED_DICT); - PyDictOrValues *dorv = _PyObject_DictOrValuesPointer(owner); - DEOPT_IF(!_PyDictOrValues_IsValues(*dorv) && !_PyObject_MakeInstanceAttributesFromDict(owner, dorv), LOAD_ATTR); + assert(Py_TYPE(owner)->tp_flags & Py_TPFLAGS_INLINE_VALUES); + DEOPT_IF(!_PyObject_InlineValues(owner)->valid, LOAD_ATTR); } // _LOAD_ATTR_INSTANCE_VALUE { uint16_t index = read_u16(&this_instr[4].cache); - PyDictOrValues dorv = *_PyObject_DictOrValuesPointer(owner); - attr = _PyDictOrValues_GetValues(dorv)->values[index]; + attr = _PyObject_InlineValues(owner)->values[index]; DEOPT_IF(attr == NULL, LOAD_ATTR); STAT_INC(LOAD_ATTR, hit); Py_INCREF(attr); @@ -3721,13 +3720,13 @@ } // _CHECK_ATTR_METHOD_LAZY_DICT { - Py_ssize_t dictoffset = Py_TYPE(owner)->tp_dictoffset; - assert(dictoffset > 0); - PyObject *dict = *(PyObject **)((char *)owner + dictoffset); + uint16_t dictoffset = read_u16(&this_instr[4].cache); + char *ptr = ((char *)owner) + MANAGED_DICT_OFFSET + dictoffset; + PyObject *dict = *(PyObject **)ptr; /* This object has a __dict__, just not yet created */ DEOPT_IF(dict != NULL, LOAD_ATTR); } - /* Skip 2 cache entries */ + /* Skip 1 cache entry */ // _LOAD_ATTR_METHOD_LAZY_DICT { PyObject *descr = read_obj(&this_instr[6].cache); @@ -3798,9 +3797,8 @@ } // _GUARD_DORV_VALUES_INST_ATTR_FROM_DICT { - assert(Py_TYPE(owner)->tp_flags & Py_TPFLAGS_MANAGED_DICT); - PyDictOrValues *dorv = _PyObject_DictOrValuesPointer(owner); - DEOPT_IF(!_PyDictOrValues_IsValues(*dorv) && !_PyObject_MakeInstanceAttributesFromDict(owner, dorv), LOAD_ATTR); + assert(Py_TYPE(owner)->tp_flags & Py_TPFLAGS_INLINE_VALUES); + DEOPT_IF(!_PyObject_InlineValues(owner)->valid, LOAD_ATTR); } // _GUARD_KEYS_VERSION { @@ -3914,9 +3912,8 @@ } // _GUARD_DORV_VALUES_INST_ATTR_FROM_DICT { - assert(Py_TYPE(owner)->tp_flags & Py_TPFLAGS_MANAGED_DICT); - PyDictOrValues *dorv = _PyObject_DictOrValuesPointer(owner); - DEOPT_IF(!_PyDictOrValues_IsValues(*dorv) && !_PyObject_MakeInstanceAttributesFromDict(owner, dorv), LOAD_ATTR); + assert(Py_TYPE(owner)->tp_flags & Py_TPFLAGS_INLINE_VALUES); + DEOPT_IF(!_PyObject_InlineValues(owner)->valid, LOAD_ATTR); } // _GUARD_KEYS_VERSION { @@ -4026,17 +4023,16 @@ // _CHECK_ATTR_WITH_HINT { assert(Py_TYPE(owner)->tp_flags & Py_TPFLAGS_MANAGED_DICT); - PyDictOrValues dorv = *_PyObject_DictOrValuesPointer(owner); - DEOPT_IF(_PyDictOrValues_IsValues(dorv), LOAD_ATTR); - PyDictObject *dict = (PyDictObject *)_PyDictOrValues_GetDict(dorv); + PyManagedDictPointer *managed_dict = _PyObject_ManagedDictPointer(owner); + PyDictObject *dict = managed_dict->dict; DEOPT_IF(dict == NULL, LOAD_ATTR); assert(PyDict_CheckExact((PyObject *)dict)); } // _LOAD_ATTR_WITH_HINT { uint16_t hint = read_u16(&this_instr[4].cache); - PyDictOrValues dorv = *_PyObject_DictOrValuesPointer(owner); - PyDictObject *dict = (PyDictObject *)_PyDictOrValues_GetDict(dorv); + PyManagedDictPointer *managed_dict = _PyObject_ManagedDictPointer(owner); + PyDictObject *dict = managed_dict->dict; DEOPT_IF(hint >= (size_t)dict->ma_keys->dk_nentries, LOAD_ATTR); PyObject *name = GETITEM(FRAME_CO_NAMES, oparg>>1); if (DK_IS_UNICODE(dict->ma_keys)) { @@ -5315,19 +5311,20 @@ assert(type_version != 0); DEOPT_IF(tp->tp_version_tag != type_version, STORE_ATTR); } - // _GUARD_DORV_VALUES + // _GUARD_DORV_NO_DICT { - assert(Py_TYPE(owner)->tp_flags & Py_TPFLAGS_MANAGED_DICT); - PyDictOrValues dorv = *_PyObject_DictOrValuesPointer(owner); - DEOPT_IF(!_PyDictOrValues_IsValues(dorv), STORE_ATTR); + assert(Py_TYPE(owner)->tp_dictoffset < 0); + assert(Py_TYPE(owner)->tp_flags & Py_TPFLAGS_INLINE_VALUES); + DEOPT_IF(_PyObject_ManagedDictPointer(owner)->dict, STORE_ATTR); + DEOPT_IF(_PyObject_InlineValues(owner)->valid == 0, STORE_ATTR); } // _STORE_ATTR_INSTANCE_VALUE value = stack_pointer[-2]; { uint16_t index = read_u16(&this_instr[4].cache); - PyDictOrValues dorv = *_PyObject_DictOrValuesPointer(owner); STAT_INC(STORE_ATTR, hit); - PyDictValues *values = _PyDictOrValues_GetValues(dorv); + assert(_PyObject_ManagedDictPointer(owner)->dict == NULL); + PyDictValues *values = _PyObject_InlineValues(owner); PyObject *old_value = values->values[index]; values->values[index] = value; if (old_value == NULL) { @@ -5389,9 +5386,8 @@ assert(type_version != 0); DEOPT_IF(tp->tp_version_tag != type_version, STORE_ATTR); assert(tp->tp_flags & Py_TPFLAGS_MANAGED_DICT); - PyDictOrValues dorv = *_PyObject_DictOrValuesPointer(owner); - DEOPT_IF(_PyDictOrValues_IsValues(dorv), STORE_ATTR); - PyDictObject *dict = (PyDictObject *)_PyDictOrValues_GetDict(dorv); + PyManagedDictPointer *managed_dict = _PyObject_ManagedDictPointer(owner); + PyDictObject *dict = managed_dict->dict; DEOPT_IF(dict == NULL, STORE_ATTR); assert(PyDict_CheckExact((PyObject *)dict)); PyObject *name = GETITEM(FRAME_CO_NAMES, oparg); diff --git a/Python/optimizer_cases.c.h b/Python/optimizer_cases.c.h index df73cc091de..b4a1da8aec1 100644 --- a/Python/optimizer_cases.c.h +++ b/Python/optimizer_cases.c.h @@ -1104,7 +1104,7 @@ /* _LOAD_ATTR_GETATTRIBUTE_OVERRIDDEN is not a viable micro-op for tier 2 */ - case _GUARD_DORV_VALUES: { + case _GUARD_DORV_NO_DICT: { break; } diff --git a/Python/specialize.c b/Python/specialize.c index c1edf8842fa..f1e32d05af7 100644 --- a/Python/specialize.c +++ b/Python/specialize.c @@ -188,7 +188,7 @@ print_object_stats(FILE *out, ObjectStats *stats) fprintf(out, "Object allocations to 4 kbytes: %" PRIu64 "\n", stats->allocations4k); fprintf(out, "Object allocations over 4 kbytes: %" PRIu64 "\n", stats->allocations_big); fprintf(out, "Object frees: %" PRIu64 "\n", stats->frees); - fprintf(out, "Object new values: %" PRIu64 "\n", stats->new_values); + fprintf(out, "Object inline values: %" PRIu64 "\n", stats->inline_values); fprintf(out, "Object interpreter increfs: %" PRIu64 "\n", stats->interpreter_increfs); fprintf(out, "Object interpreter decrefs: %" PRIu64 "\n", stats->interpreter_decrefs); fprintf(out, "Object increfs: %" PRIu64 "\n", stats->increfs); @@ -197,7 +197,6 @@ print_object_stats(FILE *out, ObjectStats *stats) fprintf(out, "Object materialize dict (new key): %" PRIu64 "\n", stats->dict_materialized_new_key); fprintf(out, "Object materialize dict (too big): %" PRIu64 "\n", stats->dict_materialized_too_big); fprintf(out, "Object materialize dict (str subclass): %" PRIu64 "\n", stats->dict_materialized_str_subclass); - fprintf(out, "Object dematerialize dict: %" PRIu64 "\n", stats->dict_dematerialized); fprintf(out, "Object method cache hits: %" PRIu64 "\n", stats->type_cache_hits); fprintf(out, "Object method cache misses: %" PRIu64 "\n", stats->type_cache_misses); fprintf(out, "Object method cache collisions: %" PRIu64 "\n", stats->type_cache_collisions); @@ -479,12 +478,11 @@ _PyCode_Quicken(PyCodeObject *code) #define SPEC_FAIL_ATTR_NOT_MANAGED_DICT 18 #define SPEC_FAIL_ATTR_NON_STRING_OR_SPLIT 19 #define SPEC_FAIL_ATTR_MODULE_ATTR_NOT_FOUND 20 - #define SPEC_FAIL_ATTR_SHADOWED 21 #define SPEC_FAIL_ATTR_BUILTIN_CLASS_METHOD 22 #define SPEC_FAIL_ATTR_CLASS_METHOD_OBJ 23 #define SPEC_FAIL_ATTR_OBJECT_SLOT 24 -#define SPEC_FAIL_ATTR_HAS_MANAGED_DICT 25 + #define SPEC_FAIL_ATTR_INSTANCE_ATTRIBUTE 26 #define SPEC_FAIL_ATTR_METACLASS_ATTRIBUTE 27 #define SPEC_FAIL_ATTR_PROPERTY_NOT_PY_FUNCTION 28 @@ -558,6 +556,7 @@ _PyCode_Quicken(PyCodeObject *code) #define SPEC_FAIL_CALL_OPERATOR_WRAPPER 29 #define SPEC_FAIL_CALL_INIT_NOT_SIMPLE 30 #define SPEC_FAIL_CALL_METACLASS 31 +#define SPEC_FAIL_CALL_INIT_NOT_INLINE_VALUES 32 /* COMPARE_OP */ #define SPEC_FAIL_COMPARE_OP_DIFFERENT_TYPES 12 @@ -829,11 +828,7 @@ specialize_dict_access( return 0; } _PyAttrCache *cache = (_PyAttrCache *)(instr + 1); - PyDictOrValues *dorv = _PyObject_DictOrValuesPointer(owner); - if (_PyDictOrValues_IsValues(*dorv) || - _PyObject_MakeInstanceAttributesFromDict(owner, dorv)) - { - // Virtual dictionary + if (type->tp_flags & Py_TPFLAGS_INLINE_VALUES && _PyObject_InlineValues(owner)->valid) { PyDictKeysObject *keys = ((PyHeapTypeObject *)type)->ht_cached_keys; assert(PyUnicode_CheckExact(name)); Py_ssize_t index = _PyDictKeys_StringLookup(keys, name); @@ -850,7 +845,8 @@ specialize_dict_access( instr->op.code = values_op; } else { - PyDictObject *dict = (PyDictObject *)_PyDictOrValues_GetDict(*dorv); + PyManagedDictPointer *managed_dict = _PyObject_ManagedDictPointer(owner); + PyDictObject *dict = managed_dict->dict; if (dict == NULL || !PyDict_CheckExact(dict)) { SPECIALIZATION_FAIL(base_op, SPEC_FAIL_NO_DICT); return 0; @@ -1258,15 +1254,8 @@ PyObject *descr, DescriptorClassification kind, bool is_method) assert(descr != NULL); assert((is_method && kind == METHOD) || (!is_method && kind == NON_DESCRIPTOR)); - if (owner_cls->tp_flags & Py_TPFLAGS_MANAGED_DICT) { - PyDictOrValues *dorv = _PyObject_DictOrValuesPointer(owner); + if (owner_cls->tp_flags & Py_TPFLAGS_INLINE_VALUES) { PyDictKeysObject *keys = ((PyHeapTypeObject *)owner_cls)->ht_cached_keys; - if (!_PyDictOrValues_IsValues(*dorv) && - !_PyObject_MakeInstanceAttributesFromDict(owner, dorv)) - { - SPECIALIZATION_FAIL(LOAD_ATTR, SPEC_FAIL_ATTR_HAS_MANAGED_DICT); - return 0; - } Py_ssize_t index = _PyDictKeys_StringLookup(keys, name); if (index != DKIX_EMPTY) { SPECIALIZATION_FAIL(LOAD_ATTR, SPEC_FAIL_ATTR_SHADOWED); @@ -1282,10 +1271,16 @@ PyObject *descr, DescriptorClassification kind, bool is_method) instr->op.code = is_method ? LOAD_ATTR_METHOD_WITH_VALUES : LOAD_ATTR_NONDESCRIPTOR_WITH_VALUES; } else { - Py_ssize_t dictoffset = owner_cls->tp_dictoffset; - if (dictoffset < 0 || dictoffset > INT16_MAX) { - SPECIALIZATION_FAIL(LOAD_ATTR, SPEC_FAIL_OUT_OF_RANGE); - return 0; + Py_ssize_t dictoffset; + if (owner_cls->tp_flags & Py_TPFLAGS_MANAGED_DICT) { + dictoffset = MANAGED_DICT_OFFSET; + } + else { + dictoffset = owner_cls->tp_dictoffset; + if (dictoffset < 0 || dictoffset > INT16_MAX + MANAGED_DICT_OFFSET) { + SPECIALIZATION_FAIL(LOAD_ATTR, SPEC_FAIL_OUT_OF_RANGE); + return 0; + } } if (dictoffset == 0) { instr->op.code = is_method ? LOAD_ATTR_METHOD_NO_DICT : LOAD_ATTR_NONDESCRIPTOR_NO_DICT; @@ -1296,8 +1291,12 @@ PyObject *descr, DescriptorClassification kind, bool is_method) SPECIALIZATION_FAIL(LOAD_ATTR, SPEC_FAIL_ATTR_NOT_MANAGED_DICT); return 0; } - assert(owner_cls->tp_dictoffset > 0); - assert(owner_cls->tp_dictoffset <= INT16_MAX); + /* Cache entries must be unsigned values, so we offset the + * dictoffset by MANAGED_DICT_OFFSET. + * We do the reverese offset in LOAD_ATTR_METHOD_LAZY_DICT */ + dictoffset -= MANAGED_DICT_OFFSET; + assert(((uint16_t)dictoffset) == dictoffset); + cache->dict_offset = (uint16_t)dictoffset; instr->op.code = LOAD_ATTR_METHOD_LAZY_DICT; } else { @@ -1729,8 +1728,8 @@ get_init_for_simple_managed_python_class(PyTypeObject *tp) SPECIALIZATION_FAIL(CALL, SPEC_FAIL_OVERRIDDEN); return NULL; } - if ((tp->tp_flags & Py_TPFLAGS_MANAGED_DICT) == 0) { - SPECIALIZATION_FAIL(CALL, SPEC_FAIL_NO_DICT); + if ((tp->tp_flags & Py_TPFLAGS_INLINE_VALUES) == 0) { + SPECIALIZATION_FAIL(CALL, SPEC_FAIL_CALL_INIT_NOT_INLINE_VALUES); return NULL; } if (!(tp->tp_flags & Py_TPFLAGS_HEAPTYPE)) { diff --git a/Tools/cases_generator/analyzer.py b/Tools/cases_generator/analyzer.py index ddafcf99ca1..4261378d459 100644 --- a/Tools/cases_generator/analyzer.py +++ b/Tools/cases_generator/analyzer.py @@ -359,11 +359,10 @@ def has_error_without_pop(op: parser.InstDef) -> bool: NON_ESCAPING_FUNCTIONS = ( "Py_INCREF", - "_PyDictOrValues_IsValues", - "_PyObject_DictOrValuesPointer", - "_PyDictOrValues_GetValues", + "_PyManagedDictPointer_IsValues", + "_PyObject_ManagedDictPointer", + "_PyObject_InlineValues", "_PyDictValues_AddToInsertionOrder", - "_PyObject_MakeInstanceAttributesFromDict", "Py_DECREF", "_Py_DECREF_SPECIALIZED", "DECREF_INPUTS_AND_REUSE_FLOAT", diff --git a/Tools/gdb/libpython.py b/Tools/gdb/libpython.py index 656667ac939..74165acd831 100755 --- a/Tools/gdb/libpython.py +++ b/Tools/gdb/libpython.py @@ -66,10 +66,12 @@ def _type_unsigned_short_ptr(): def _type_unsigned_int_ptr(): return gdb.lookup_type('unsigned int').pointer() - def _sizeof_void_p(): return gdb.lookup_type('void').pointer().sizeof +def _sizeof_pyobject(): + return gdb.lookup_type('PyObject').sizeof + def _managed_dict_offset(): # See pycore_object.h pyobj = gdb.lookup_type("PyObject") @@ -79,6 +81,7 @@ def _managed_dict_offset(): return -3 * _sizeof_void_p() +Py_TPFLAGS_INLINE_VALUES = (1 << 2) Py_TPFLAGS_MANAGED_DICT = (1 << 4) Py_TPFLAGS_HEAPTYPE = (1 << 9) Py_TPFLAGS_LONG_SUBCLASS = (1 << 24) @@ -493,11 +496,12 @@ class HeapTypeObjectPtr(PyObjectPtr): has_values = int_from_int(typeobj.field('tp_flags')) & Py_TPFLAGS_MANAGED_DICT if not has_values: return None - ptr = self._gdbval.cast(_type_char_ptr()) + _managed_dict_offset() - char_ptr = ptr.cast(_type_char_ptr().pointer()).dereference() - if (int(char_ptr) & 1) == 0: + obj_ptr = self._gdbval.cast(_type_char_ptr()) + dict_ptr_ptr = obj_ptr + _managed_dict_offset() + dict_ptr = dict_ptr_ptr.cast(_type_char_ptr().pointer()).dereference() + if int(dict_ptr): return None - char_ptr += 1 + char_ptr = obj_ptr + _sizeof_pyobject() values_ptr = char_ptr.cast(gdb.lookup_type("PyDictValues").pointer()) values = values_ptr['values'] return PyKeysValuesPair(self.get_cached_keys(), values) diff --git a/Tools/scripts/summarize_stats.py b/Tools/scripts/summarize_stats.py index 8dc590b4b89..f7ed98ff604 100644 --- a/Tools/scripts/summarize_stats.py +++ b/Tools/scripts/summarize_stats.py @@ -394,7 +394,7 @@ class Stats: return result def get_object_stats(self) -> dict[str, tuple[int, int]]: - total_materializations = self._data.get("Object new values", 0) + total_materializations = self._data.get("Object inline values", 0) total_allocations = self._data.get("Object allocations", 0) + self._data.get( "Object allocations from freelist", 0 ) @@ -1094,8 +1094,7 @@ def object_stats_section() -> Section: Below, "allocations" means "allocations that are not from a freelist". Total allocations = "Allocations from freelist" + "Allocations". - "New values" is the number of values arrays created for objects with - managed dicts. + "Inline values" is the number of values arrays inlined into objects. The cache hit/miss numbers are for the MRO cache, split into dunder and other names.