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 2fb7723f58..e74fdd4d32 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 e004783ee4..6c90c9e284 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 ef59960dba..5507bdd539 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 0b17ddf0c9..4fc5e9bf65 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 de525f72d3..aa87dc4138 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 bcb10ab723..54dc6dcf40 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 51206cd4ca..0f2046fb3d 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 96790844a7..13443329df 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 04f17a9ec9..1958ecc0df 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 0cf06243dc..4c18141427 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 5fb4b815c9..8829c9a6d8 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 0000000000..5974b1882a --- /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 5084bcadb1..cad21bdb4d 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 e9db6e5683..b2af47d05e 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 c07652facc..d6d50e75b6 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 536746ca41..58a3d97933 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 b4f0fd4d7d..60642d899b 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 4f379bed8d..352409425e 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 c0068d7856..731a25332b 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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∾`=h*V?&OrKeJ&10xH~GgXvU<<5`@ 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 XH*Wo&I&bd<1`zOc^>bP0l+XkKYXwxy literal 30688 zcmeAS@N?(olHy`uVBq!ia0y~yU@B)|U`*m*V_;yIV)^en0|NtFlDE4H!+#K5uy^@n z1_lKNPZ!6KiaBrYR>t&J{;~A0XGlBG@fJEjkX!xT%_o;sYT3d_O#3Y{{4M;y|jFplDD7V`{$1f<@$fc z%zs+D&hFLQ;@IoYJ32ZJNF?_d9-HF0qid0JLXY7wiR65K^>Q%7t7WqFF_5(Cp_8&8 zRWpPhKIsD~@#!omIu4Rq=3=oEBqI^5H0O@tF{!nem-{=b2yyEB`=6J3Eh#B^^6KiB zTN4g4?KBhWe6lpxMM-d;U2RnE9!_DkD?9a)d;SWmm-lRRTV4MC-ib43j?6O6zOcko zcuo9%Is1P1h+ZmR|j}_SUf@M_3pxY{?8Z$-O1=>eZ_kx3+4BgobAKz7Bn5bSxqi6b?83Z_m1_ zHD&75#3v^vdieN+lvwl_ev1Ny)yDtl4v$H#K>eQhPiH9dlp1k!(jeuI)!8lGlUdVzrZsP~z)rYb6X482aSx`z(u}U5Q%1 z?yupo8@rEy;^?pQWHsLn_5bTiUSH!qc<|tlhwbvKt_B7NGc%k>*%b9#Q%kF9+qSa3 zM^8#5n}Q=VYr*2h&G+m7%dU;u+O&MR`oABK`FB1}+9)9~{tG}-c^K&u1rmwGme4%sulW&`=r|#-8WCJH44}X7mFE6ftzh1A;Iyq(PR8c-z zs}?oiSs~ft0s;)Ova&qVW<2iwa;}b!jGsPzD){y$(!$bm;ffU;^J~A&46rvf744U| zck}e*%)7Hgu_rVnq-FpAf6|NHdW9x!4PSX$B002k#kT)qM#mzQL2P*{X5sQV+fx`Z3pqrap?|ip|63M(C7V*SVbj!9JC_VV%y2nb+c(9qCu@bu(lP%t+a4+{$u=sw!T z({Fg}l_)4ve0THk;4sU()3Ildjbh^I)2E$XU0FLkJYHle33YDBxoM;mx#>x4>uHH( zu7ms`2FLD6o9EqeSnfABZe_2Biqm8@UjZ?(w!p=1SA$b}46h}8U>5D!_}%?@pX|k@ z-r_4(TzMNleby`~9$sESQPHEFvdL>Mf*krSZl+Nxmy6Ox@7Jc8mssB3+UlH>qf_ETzUrAwD?*uK5JuaB>(x%su~wB`Qu_kFFqIeX&Clp7n9*=1y8 zF06~Sc5!n{`u65##g`X?pqw{R+1<(a?a!lZqN1Wcb1Ve2va$k#gPmua`a3~)c(r=H zLFK0u32Eu;EBUpxwR3K6VtstPzkSuJEL-<863OrR<8~(JpUvO@x2@mq7e`c7)FJuT zfByWDdM#&J#3En!LvhR2tryqF+uzw$%FPfE7N%DI^V-?7zU%J3yu3UyJ)Pax*LO?q zZ81SX!J^~m=U6gVS663bXCFRtge5O8@5JfThu6jKHb^+Y@Z{-JMusIpnl4I$@9yjj z=1*_Bs-~{ae!u>|?X4}D&54KG6!rD_tG~TDcz1XC!`H8)pC4IwX^H1#i;c-W8|xoD z>Dw#1|KBfdyT4z8MMOj%9BgL)@cFZILV|*ftZZOZRMgskbFIq-L`7RqPF8>XL9AlrI+cy8Rdk?`fk#UG!~+cPp;ym%2Lb>+&H z4-XD9d#DJtw6z^NeVTjSx^*+`>-Sv^W3y=S;-~MHCifVAduFjSIe*QbJu;iq&$9_8-q?_6U}kn~N8w{oEf=%1 zh;`w@g;#H#nPti?)X4%;!?0xe^5oCY&N4JyTN{0Shv(Y%cJ@=JP8sChGI{ap)uEd= zC0$ykYKJp1?Afy?AS#N>+}zw_X;4K)g@d;@_p)WnzW#U@nS1KgDFHdTzIS(bA3k=B zO_NT<(NdU*PJ^W&A(lbuYdUPa63c8GT+%u75`?=`}OM= zLqjt=|Fv7&Uip3y*Na)P_1@dt+b7PL!NE{aP|)C^(zJQAF@u7h9$&wlt;zP9$YiQCh|;-N2Jyx@pGe(cyjm85TGQ>RY{1^I?e zn-0CYy87X>XKhQCsQmeGn192TEg@htUx3Vv*q(Q{<@YTub9OyV&4ZiM&oeMEv+=CB z=B758vEk0;ReE}QTl*3-Gc|2Ich>$c3keB1A^ZN$&dmijIyydjCw27n*cj?oZIY9* zD&d%GU4HD$OyiF~pU;1MZmxC2&LUMGA0H7}spKBhC{V;KTehriYxZ@fihZ61Zf8}0!X`$ia?fw1y44}Y>ul*{zY15{H zudhP?e7WqOacK!>*xD#oh8^|)?e5&YJ8|mN*6i!+4&J{n&v0Q=s`tYEv(59B6ciq` zPH&2Q8x$1u;LV$!#KUb1OJ85xQTUkc+WL6??T1cDB>OmaY}~G5WHf14$xA0GDXEl{ z6c=H2za{Ga^Ei(0|Mf~cYG;wEtYuM)-LDtQm#$q~W|n*F!uI=h-cO%CEvn7T$Pi$d zWmCB+rqaQIVQbdat}9osuJoO4wk7SX)Xr_Ux92Y}dwYxX`2L^Iq_uQ)Rh1S^nj|#K zqEIO_J3ILIx3@3f+}!-N!fZ-#W~L@9E9=C*KEAEFx2N^V+phyPpL$N-)>TppDrq`- z!~W&#*N<;*P6uVox<4P?UE2?T__OlG#^mFVKta7FL-5)8`TV7&rLOHqC6ayOKvgyG zv5Cs=p!#Rc8XXBqNk#^{9}k#W-H)C+eR^Zn*Q_ruE;0*svRq#ue|(W^H@F;P>)PI$ z&g)iFV>8dDvI*4A+_Oi9!Jy(p0;~Jg9kvx87}l*@x1;8#QP`S@#>&snuI@N!0;)cn z>%BL#9X@-Omzj;{z=wy20|Nsad!@}6g|Cn6;#+;us^oOUI`R8t)~#CyYTf+^R+UJ8^H=cP^i;{@rY~PgcI?`vWMw5~ z|K|hqox68MWrdP^HijQLDVxm0Q1I(ZX3fv1(=S}Sc(9dQ{KC%Sba3PHjp+wolOEF| zmljd&um$!1{}mJzIOOE$Kx}^e#ILhQ#!^UWVn_V@liEf`K^KGBZr;2pVOL`@O+UWx zzySvr7Z-={@bHU?hQ}nTnQJ1$PTshAGx6P>on3s*dyjP6xqpxC?Cx&fyxF*`tE=GE z70sGIACGtObs8SK5q;p~+({cZY&ftYaIuIiTQ#SvdU;}E;taD~shpe~0YO2=;Nala zV}~V@-@JWzdQ*?7lF>|_ckkXw*w@)WTkSO7>4mzJnb`74UdcKYiMA&ar5TE6DK(8|9%w*d2!hhu%wu{ID2_{`Jb=X z<0H1^NbcFYcj2m4t6pr7Nbb3+9Je!>_Y$N!;tmcDR#8{Ke%s>X$B&@aPf2NMVp0-Q zet!OxsZ+1su6Xa|=g0T#{QUlRcXvBGJ2Qu`k854OULR!DHI9O!<5DXA{{CxXcZ>PY zvst;_5R~N}JYewm_qVC~!twZc|M6>UqbE+96qKKQ&3vX0TYY_fL{wDL?{9B8`1p?9 z+?@V#ef?kWiwiZ^W?o))@@;%XNXQaU7U-2xFJI$mRrAAO&)&VSOM5nKFqkrZdh&$@ zjuSjoK79Xv{PJ@Di;G;lt7Ny^fohqjr>16HUdH?9&mV*2V?1|u7PAL0_iOb~2`Z`Z z%Dua*RKl)i$D+dtJ%)_;i;qj`I6EJ%dTXz#$@%KltFKmWwOac6$8T;<-Zf>6WakUKbM$j?HKYe|N)sYtGG}9Z$ZzTs|L^35%vL)DrGo z^)xOnj-lbi36F^AXiz|fg@yh2`FwtBoxEL52dIUmHPuN+Y;pSec{{4Ut}6I5U)!%+ zTz{H$`Tu`^KYaYyxN@bYoLvovy1KfAl+>d~N4xc9ejn)+7U*>0;N)~nOIvm!zNpAZ zNLcvd>TrDz6`>zLek9!9mRs@XN8#Ri_rpJac^M39k4;wdZR+gg{Ppc^_l_MFYvT9I z^~>2F+J3+8bZ4w=>8mTAd3HB%-ehE$rW>s`G&ZP0h_ufBAWNtum`$eeZVVuP>Q5Zr*eZ4i?V6z0K9drKRA_m6gFS?(8)F^7ZS+ zf`?9^_E%?T=hu=jiDa+F8J7ym%GA=+pKrhS{_=AF%m)V=mn>W6_VVRR7ndn>=lXv8 z_6=0vva+%|-7S56?c%Get5>dA(XlP>t`{F4-;^0MJR~J0O_Gjq1O)~zym2E!Nomu@ zjSK(%{T&eFF!my{N?ZW`p zJ~gj5nu>jYcUSnFCWDuspPJI5O`D9Q43k(|TUr7lBPD_U5LOz0u5`Q>RXyID6JtV|V?3yQx#AT-cC!xMkC&OF>FXR#jg#R<2w* zarJ6#+v;yU`+hv)7V2~w-OeEj@%F*_GcC_W}p zt^elP6Ti+Y;YIE)BErJX3>rE*J{q@o6e=?=I6YmTJ9XWG@avOx8#YW}xUkfFddsHL*VnFQ7k&Tve17o%e}7LNJlF`TK(nr|la-W| zS1n4#t~L*a^{ znVA~Hma4B=N=mcLa-|p*LPJ9rp0l59t+eRr>FK9c%UxWI)6cD$b|+MAMqpr|i_4b$ z`*utY&TTvky{GF5G8h>cEa(>37ZSWU&$jvnsM4)Yl1P5P`EJd5+10|<*47b`k%|BR z{gp6I<6%%RGZSNDW9#VYIpQu~+me2Mo}<=OCs2{MF2=E{%F3$bMZnti(&l*_Zy6sP zYUPgDUuRqQ?`L{OR#w>R{gt27K!x=6_3`V=&YU>G!2s%KEqrrpYqq}ZZndbWsEk`% zG=F`0$;{8sUr|w!aBWSbzLjqN8qdjU8FzP?nwXd@Si6>2R#p}?Qj>9U5o^@etX47I zCn0H`0DEFi>pGlb)vR#h>D7?FPIj0cAjmufRIpA>FaBWUte8~ z*i&Jcc5V)*y1IJFn;R2_cb6Zyd|CLwXIp;#_iz$It-cfE>*?7Tp>r&M|6jJ?;NU6KrX}6nl=|ZGa{f=B zKHbZRIX0u{@sqx*H*aiAR-AmY;LD4^H+Oal2QT+KdZbgh@c+Nsm|Z2DoSdBCp)-g0 zc=-dLQ&y_|{P`2qf!MdtE-5+r;j33sk5|cl_`_Dt&dz>juJv|5^GW-2-iUA9zTG`3 zNr{P>IWZ%nBY)pdHb+Ot^>_HI>v!zh#ie}!)UMId(OIzQ)BgD0&d$ya8#f-DV_CeR z^mW*u-}nEo3*I04dqpp(`M>T!rk)n7`F1ni zY38zJYSVP1+a5hi`tjq(fjf6(ggRON{`z|O!^6XgX=!W>H@0Sr7Zn$SQt=$?a=zBq z)*Y3f)$0ELEuUjq%y!^&E~tR%nrmIIWM(E7yxh;VveJ@^i>u(%6Hf+*sHmv9$Jyrn zzu42&4n_GjDqR{Q1+Q zFO`zK0`TY90gsAuBcn-6 zf;2ss2F26Vr;&)nQB4 zt@F#hwPm4c_O*b@O3SHJr(T?->aAg7G6mEQxN&2~G@ZyG{o+Z6%S_6;JMMkxOSqO0 z66d$7=0KxqjM797K|w(Y)2u0roD+Ktm&F|Vxk(2!%ujFvKq9$EQ_$}nXlmft6b{%t zf{X7(#tqw_|M~gZ!_TkHLnWw0V+~(YQc}hI-R2^alAPk=;;!u&6$D;b5foaIdSnJG)NgCYOs3H?VmG7OQAza41dmsOXh6Ugr0f zPu5E1`nhxG*3DB+?J-T6bH~(abJ|%a2M2~rmo7ay)%jxT_Y>TFd~SYz=Wg??zxVgo z>-96N%k>s5UR=3drWOWR<-lXx2<2lK4xZPBV$ld(1wJAO;+E70|Gw8 zlla6!<-q@ZmpJFuA=~1Rf@sacA`8_>7Ie2+n8ygvOa&rx7Mb|z4{CqwiG!naQo7t|iw_4MuoJ=ulX>C1t@#4l?UH3p8^z3YIU0vNR zSy#DUUt7z}uw(b`5xKnFUtI6` zy?wRPc6N3Krlv;^xAQ-I@`T0N**RuU#l|T6x3{-X-@X0C`~Cmf7+6?XI8uLqdwckB zJHKJk6AzE2K^;evzP!099KJqI^w-blPt#3JO&8YsCWqGMPn79?dK$d1ijtkf0|Ohoy11rHoA&6#!@~uIg^eysjhiTOj_q@KbF?q@A)!yv|oJN7x|^3S9m&AE5~d`#{=aAjri#ogukXXe|>GZ+*-@%Z!cxI70ZCnE!B zP_y>;x7OCy)(gva?2wo@Z(hX4Bvu(283u;S%Y2*dv4TWP+M%qtXYfV_t$~O_Op9~l_r8lYj%{ro)(ankkIh&@9)Ql zTDch-Hf=J}iP*r9er}HAY_nVjn|gr<2O60pHmC7!%e&ikdb0T)EP+c(L*{ zz1UxeJ|C8!9crGMn#wTme)YEW^Kvb%tqU_RFWazn>)NpJo6kI+R{wauRz#?ih2g`K zlas}_rT3WHfyR0Me!U+5>nq4E04 zN?{{o%UyNdUd$=}jZg^Z=TwJzK&&FfD;qRuzJk>gEKY4q7P~gP#pD$m&%*Y@qDVcBbubG{nfuXgv zwdiSDTH1wc*W9wQv>IN0H{W|^j%Bey&JBYjM~*zW|No!)p~Hta=G-)TSmEWxb?45V z3Dc&v{rvpg+1HnM|G!_arT0!hRXlOd9G>Fh&r+{JGfH1xT#VRTWqNI0Z1>O4&y#`R4x7YpsmH7VN-mU*)rl$6V zsMXD!IkV{L&6_uij!P!55n6vobLXl5SFe}N+-%0i#>UXFVZ#P||tQ7OY?2f8$2Pj$OM>tzEZA zueP?9LErDho40R4GgJo-IDm#+X6Ns73<$U&-G5hN<*HRquCA=FUcHhqOk%0~{nk7s zEiLJIpX`hK`~Q0jJEhK4mbETxnKw^PNLcvksrPqwGBdomy862G@v{=adUNh*T1H1l zuUN4n;rqM0D^{#nQMRXO>B3J%Ay4=E-rZf^9--q_QDHGnFSbkFf1ZNAK0m0Qn?GN^ z;s4rg7r4zT-`?7)Jm?_8u=xA?zq!4g zKl}Q+qb;1mEy;m_fuJFAHQ!k*pg!`RtU3Q{_A`JcW*8=PZ!VJV>*?Vsd3lNT*RNk6 zzJ6`pzu!J6IC$dh+1;Dd&x2-!TwPs3+1|v|)G+;=jEaiN0_S$Vj?T`Nm#dS`hffF% z2?3eKBWoohC@6U1$%ER;!@l!_bSFT z;Yl%)EGa43kaAK;K=99>KM7e`T(-8hpwZrfvNE<0HQXW(e(v>--j?I&<>j^Yqp69B zfPg^5oH;U}@nTa`(ag-u8FsZ+YFMbrA|f1*|9=iPj!QYz!U;<8F)?%Qa4ncQ zQ<5R!`nuQ?XU`tJx3{|R-5pEN%+Is4vn^_WnfT4MVtw#a{^u!n7ad()M$kM3Xofo@ zW5oye>&K3Gtdf$F76lI&=FFYDaOu*ow`#Rc%S3L=k@WHPH7tJ@ z^W^E%hetYvIk>nEb%|~=r?YNzEnTywM@%oqBWUH6l9G~)H#atd z>cUr-mU8Fb*wDBw|GwV#w=G+@ZoOcpq0s>v__DIHGD$eVu&d~4SAmYP@nld}d6L)C zE!E%Oi67gRf8TD?wry&>yu4ZW_E_r1?3mEi)z#6_vQ~v*0<_)#)Asyq^Zq_L+o-j7 zZr`5$<;#~9!OQ)&)c>#J;^tP~7~xa={M^#R?fj?r#upbCD=BH|=qx#J|9_5&sp-n( z<9#Ksu4qPWOk%xx^JZX1hQ`z9&xHjqF7usz;?ku_TefUjQT_d0$)h8jN>%$x-7q>8 zD;F(V6jk$JZNQV7|9`)m=1G1wD!qB@aqTQ+)7;jem-qHon`B&208P?-dwcuxx3{;m z*Bp;tXmIVtTQR|p?b%x0r%oGJe}5NRpzh51q&2(;`aP# zm2TSKGkdo5qsNa6|NZ%yb$rU(8pC5*BH-Bw(7Gac9{{|5CEJ0Ei>rlq!tox-;N@oR ze6m8fTH4yK6}R+kZ0j>OOzYXmmUM28Ul@SsWD_~kKTnK<@ zQ9S>1S7%SplP7%}-5TEGb{wqx(=yrm+JcnS)Psi(F|7_;TebEZwuytiPc>$4nhala zup#B-q;j*ofPe`fBsZ@AeRGr2M2@nuvad3l$!ior)29J}fr3rV&58N>{KCS*8JU?2 zV|SN*`1;i~C`c%5T}PFCN4xk56TmtVbnO&Dm{X6sf^iK^y1t7Ucg zdL?UX>HUAdNq_nB<-@~vc~CpKC^so72{c2Mb88DHXi8H^NNB>WSzYh<|Cf7yZf^6| zt)`bQU(S}jeBb~BX#8VC!a?oh(qdw5pFS1wNSSa5b+Y{X_pjpD7tN?G86Qv8KE=_o z`8M@;();-r7rQI#>G@quJn`}C*Q5RR|2l$~`+dwZNq*B>G2c3D@tip_JTevn3n_`6?UU#oBTD)|4e7F5Q!wYBXic`5Ye>(|!gYuB#b*pSG4YkPkG>hSf~ zyw7-gdIp4t*K61Q{P|N+Q(}dp=Z5dyyJui%=ol3xC8i(8 zqpYkvW9CdyJA$D>Sl!P-MMzOag=OZtJy<~}u(k~7H=@bdQFn0&lXe2(R^3~-0){~P=rs^E9_a{u?3I{5nf zX2f5P`1$jvQ)Nnu%9^!n7cN}LsI9I2=g;T!hc+HL;_~IyRnXc9J3G4@+j6Dl?CWBd ze_yt06_>B?+3QyNJsaIx;4>%Y@6@fWXPcOsdWMHjKXvNVt1Hi5US7Ur*)q3Tvu1^a zgnWs5{0^y`q@=WI`*wBkgvY#j^Gcqc5z`y>yQkf zbHnh`)vHUJ+4+Ow;^bUiTqZ>5s7Xsp@7xx8^!RaM1}WRBlC9r48rpayKSjM?uf-rD zDERT%Uz0LHLBTKQ)&4e%jEI;pckbMPc(uub3{hJ$1O;vXmxG$OJ3ks38=t&(Z5l&BT%4SsVD8;r zQ)OgiGz<+V8W|a79O)2TvS^Xh#*G_YTtG%*OwlMSEjn<(L8$wve$Dw`zkZpNzPb|C z??2CGW=?MI>zaKM$zJK^>+fvL0+kMn7AbAXxhb@2)vBv~?Ck8P*Y_2hxC@cP_2Z8{J3Bi&CwymDQcjLexZXq$h6k<5Mn@-mEd@;!Fq~N1 z5xQ>O&Ye3QRfHBUTEsNJ_8X|Kzqlqyh*pexHYfC1xva&L0#7|UI6x8)eF_P4a-`6uuH`*yUSh%mRFJf28%V<01o{h)) zCR>O3=H=-zJh-_z-Bj-ThYt=>QBqM`vsfSebQTF-=EKO4k(s&o;*Fq{TcRRUCxw13 znzC)18N-4tTee)=mz10gS{{>~oxQUNG={t4n}NsDNt1+j8fs0QKArhM^zQZjSLgf9 zwOR=3xqUn?-?~XVeBFs$K_O}BZb{=b(98&EIz>rIDb8Yk!>symq0gUg&AvWi-n>3O zSu2kJA6M2acz&!``og74PM4SYUR>@k|Kry$B~?|{&1q*>eZ9thOrlzL&Yg`}x3=YS zGkEyY$(RmwvkMcoj0Qv0vU_X=1|HS64Hxt`Zf~jcV!Y>N1y_Zg_0f zqbGf#m)FHw-?(vO!Ja)bii(P*S(o?KR&U9_FK20KX;J*l$JRZ<{MoZ-3=PrS^Fpmb z%V0nYp>C{8KHhiX@@40wq(w15=USIDFzl`RT6KEjre6jnF9JZz&JG?tc;f8Y)@jqG zeOQu--i6sWm0eUzUZtz2XU*2Wy?bqigoOnqBsiR%oohayHRs^ss(QQ;)NxWTU*jfi zmea6nmlbH;M)C7=S6BKzeRc#K&Xe-u;^emF-VV!_pE~{Ms&|h=Lqb>{)K7P1MEuy7eSKZVwKbBUG57B($wfs*^K7fx#KoseB?p9uAKy{LI}H2huG ztZv^~CX5U_f2vGA$zT(|_4TEt++V(aefat{cYS^Rl4Z-3%+1Bq&(CX}K3%-4t7}8j zQLc=vEKsA$!^6X&olmyqs_QX{YH5p|$)>L^E@tnOv1F2!mHqPewm7%A-jTDj%>zS1 zTDEL40nO-?zrWWCTCj2R%E^S2P(K+S=Hdn3yI^p3EE`9uD$=t}gH8<^IPHG%`PY^Cl)+=JYh(=9McoLDP48 z_Snp^C}eVUbd1=T)Ve|Ggw#GxVKs;7Xz9Yj!XoXhQ@4w}xwVyh=gyrsc9-j)n`_;E z=#bN$J(a@C{pX843k(Sn;o{^)#1VGg`1dN;81{&s ziVmE<*7n)i+3xQBa%;ohy?yIjRaJFmWw83K{q^>(t*xMWhn>aGFFicm{`A@8ojWZB z1x3ZgEB4KtScLn-K|PriL8y-*!1Sj8_;yZr_Y}|J2EaW^G(Uj^o)v%y0W*r z+^X;qi=gfOI8_Ua8Gru#IdS90j5BA?X8!#2G$b(4(MA6rSL(ezl~rb6M%lq-;M~nJ@HCvNUmF<&AHsyMLdK0$I3#JOJtgMIFPK17cT0POZosTou z$=Ug}rPHwp=jF>@Fi0f#*qW!GlX>{~TU_hp$-?*Dk4p%DIE8c9!;g8^szv|)RH~?} zJF5t3T8V_Q8yXt!Ie%Ar)t?fMd7pa>zuDuP$hcQM_4$WVIj4-Cy!-ol@7Mp2B{-D< z(lX(B&0QBax1yrs5y}U?-vMpKz&76k?rSCY7=rd~WCv{8v?(Dmu`zUY*xs<;KYmp3 zNE)&1+O^BT(6Dj)cJouGPTeb-(qnim;_%UvvegS0EmC4(XFq)Jo}8MR8mPkAUH10D z>(|{&mZ+?W*|{ld_MAB~EG#Sw-23G~O;{Ht$M^U4Zmj;E7qh=kHv8I|%7-&tzC`_= zwsgso0|yT>&YL$6v}F2NzkL7N=W@$Ey5YkMC@= zhpX4`b8>QG3JMPX`1kw$$9HxXZ`iiYt*WYOZL*Yvgo3InE4P?V#nXzzhYkhA#q~Kh zvpFhFJaGTMy#IVV*KRT0xSje8ACMaTujG?^HhyP&Uwk}k%H+w-jm+#!40>^UID&(N zA4WVq6}!LgFK8L{!ax81{+6&R(ct0bEqr-N^~LMg+{f3fT?<-q4XVz<*T=1$K4bEv zNekAk`*-cvZFp<`e$;$WXxGxSJulGdBGBpL;pfN30Ghk)>+2KfJ_?!*`SSAe#L1I` zmw#Tc3f!c&x|-T!cub1P?_GqcrKKfk3b>@S^x=~yUk>fMU-OxlLBZHqn4M3iVc9aZ zHXg~K?AnPQEcW*MrTRhZqUOz;XHor4r-Osx!Lw&=@Av)Yd-?JuXngbE=lTC1yt}&_ zwBGE_-MfKdVQv5Z{%-C71*C+OR8xn8%Z3db4t#uk+%V^c0cgbCf1b_3U8S!3x8$+E%0!82ygWMr@^ zeWjvOb@JrNL#kJ=FI*H<>vb(8Oq^fNhJisuOzhOu*)wN4Mn_ApT)A@nRsG~1!)p@? zi;l0#yuMC0CnpD#(kD!qV6`>#{ytk!KXlFazJ&#NaVt25f{@Ap@myl}@3iHjF6GBgA)_iOA3h>GgEyxgDrYbS@Mre+6x zJpwDkg$oxP92^*mii#dU){HJ$x-{|EmzNS21qyTK&W*3&>)=?qa^-}RDIUJQqLX}% zX|0Xk&L`B#BCZ!x@zm&;#A^LHcQ!_SeS2FR)Kr>3Umn!txVN|3IXoP+pNE5k8V z*Md(Gdk*Eek0(z0@;=A5~66(^tEQ1`cL=dV!EWbmPD z+V%f_s{i}@zCQWEfkum}FB%M>{Rt7f%XDRAWLj+BE%Te(wK4hlp+kq5JUu;~?&RLu z!pP7gWy%GbyKH9Xzp$$``^L?i3yYthvnY6Qz}x;&&c8oD4b08mJv}-3g`)cW-o4oD+^d*N4BJ*FCeI7Pi$>=!=zdvJchL5buw^FFAD7=U&!Y6@el zM}WIA=>5squrM(l9i0tXS6BU#>)Oly=<#F2loJA=%@#+Fu-vuq z+U20iEr6AsU0Gdyxtvl`*Mv!vnAq9bE2^p+0>@^Bi5?H$zwck} zKYx*Hw^$b+ufyZH#h}u{yLs>PJjlfRyhVpAp8L(QU|bo}_3`oX$1g4}_9{JT1X-Tj z#kV-DxAdIhF^OP3Q?>H&buo?Y?d-G6^ZTYu5wWnebkq@B>^)u2qWD=)%w6;`o?Qiz z-fEK-b#!?8`uaY6{mROqqM~x(_HAi3H8l@!@57Idb|W6bd2+uPfqR`p8s1-oAhZoLYxtFtS6 zf3MfEneE_>8xot-&K{ay|Buu4z4_lw#WAg)F_DJgmAh)wS8<()ch z+NC~OYY(M~0t}x%epFOas;;gM3Jq0dc=Gb4XZX69g>&Y}6crRGbh_{I0#@Aob47S|8joObrbkB^T{_a0@_ zR#IwWII;K2)vKHgGiS`0uzb0?tE=nP8`o~$oH=Li+}FC>#OFR1|C^)bJI`h&4=?Z2 zUp)N$%a{4izI1bQ`pOL(CZwI472C&hGf8&6`&`xATRRYNe;8O-GVm!MXA?(J== z{QUeo%e<%SsWNOSeC(#A6018p3o42-Rb26x^s|yEywci3+DyyS1 z&BcXZ&PIXZ%Te)o0l|sex0^FAc=6)Jbvxy*u;8Nb;6>M$`ASFaD$%^VuQoa(`u<7T zz_ZVH=-EpzdUY~3Fi_BQX%H7Dr{Jk|@%#PQ*x0tDo}OlPeq-e3w3DjkE-u|-x) zTRV5}e*Aj<{$mdhw;R^}D)Cr)DXUgmTAG=`#MHFaC^t8k!67p0b_5c2G_fMZPg@NJSot@27wZjjc zKhJ-6SLxv$g^vS5LR8BAUOW4Od^USl%$^ECNlD2cFBbQ=Z1S6HrD$v{%uw+2(^IRn z8)vt<-q@#y#Wrr-xTET;R!>h4 zXz6j4?B->~G1KS!`d+$x`RT3y!LiRHV)vYz11h%fx$FPwb~n9qVE)vppf$A|^+)pW z?h0MoH*1zu)b_l;)0}Gj;z0{LXMc;ox~ufHgUkD)+Db}`{N8)|zO9VQOHFNE>OK9@ zxw+OauC5mU^5sk1&p&%sUjKVjLPCO}VxOy*@4c@wAd99PF4E`E4;_`~PVt3S(RUs}S+aN_F9IoqeM zZdz~rTmsZ%xab}J?%A1{&1=@^fQIOUm-#q;e|Oig>`g>QMuvid0)wTcWkG4FYgU%l zrAwC#YJZjR$XE!RJ9mzuVY0eE6GKu`lEUPZ0WmQ>FJ5FY99S8=+`$Dj!t(NRMtSLU z*jj+)t-oAaK+ByDeD>}G&E}@1;atTT7!aV)Gj-auFnx7zK`mjcl_6c3mzRN7uAV%3 zGGcF4>DFrQ*U`DpBZ7lJx7tfT__;jT#nn~O-d=vW-&`h!7cX8UoSdYpp!DPC&x2R4 zh*VWo{rGfRA2cZQ+%hdQlaqmimlw1%+sWBkP*|9m0kjnNSg&+^_Bqof{^vl43A|df z&0Tky-(0VxjWf)0ZaA>9u`OA?+#R$==gb+OomIcTy;aW6&c1Zv!h&aKXJ0iVy*uLq~4sn!1ZcwA+wSLXM3cMny} zHp^A=_V&JX>C&QicXwaz6jtx(c=Bt4*HV-GdwZta`CSqSt&Ixv_wT=R_kVnKxm;N2 zR=a{f-31mr?UIbl>MkO#1#(PB4ULyynKS!}(eK#e6+Y53mx5;ROe@{{G)K$wNl4nt z6)P+ZGW;}~H53Gl&KD&7|EF-i*Ts>Q|H1D{Il031d#}}*FW8g)cI&lyYe4-|O+CGJ z8}drmPh0yUyJq=&--3bR^D(6kVW+tiTUN+P z^NK9}lO7Pf%qQ`18}FuxubsbL_P5`dd0FjPpX_O2f18EXPa-6h-TPQBxw3 z;X=Tt&!0KDxF&HvbK+2J5!iYp_hf8UmDROt*IL@!nL*QwVe4XkUSJjmOMid2?f9l^ z*RB}_sYA>;x$Q4lqO?P$9F1{8cDs|{R?vp}j_&Tud)>URL+7m zFYT!;_VM*ioxdjv)MGC%FRyJ^Y!O(*H|fbbzKxqVA3k@kPk-MJr~3;#xPJWpot&P& z9PIonjz-V@K#pC+H}MHR#tYxLIJwu?)-JC8{_f0N>+t&zLApwtRLY(5%ii2LcxI-t zW!0Azd(KC2D7LInnskSAucUREPWrhy7t1A_I22vxDxI8rLA$!Ty0%@RMZoEb=A=8F zS=-Cs$L%V4*|c({rtfUC)SH`9cWwF+L{Hy5-uhk%=>AoCx-}J-Q}xvsS2Di-402 zmztIHNFXvn<0Y-7Sfrd{Rl`&O>h3|{7Q@Y=O$bLPykzNW3I3EHS^VQG1BPi671 zFE5=#LPP>~9Wql<0iDf|kd)Lkf4=j~gc(V2G=HD$2$qap1+p#iw(o zemcx=pOBk-_R>=C%v)PD-|zdar>d$dI_;{<;WpmI@%!sQn-OYbc_$VhAFrgO^y5*t{)Ww)lRrK>`s4TS@Q5&BQ4x^|lO{QZ zgovb{oAdC+#l>qPH@7WxZV!ryIrHr7Y)RuZ9zH%kn}0tZgLb#Hw6?yqeq;6i!^6Wr ze*H>HOKV%aSXqDX7oo_=$nv$D6A!c1)Ye|iFo}qcHcmRyVbop8#LWEh)2BnHPIZ;v zt89P2?{{BEM+evG7J*0eG#8z#cs4VAO8UG?xBH1NQc_bjpFMll)!5i*lz-1g)zC2T zjR1?uSPin<0) z*|Wzc_2MGeU7LD(c#a-E>|FV5X8OCRu*(cy4h{}W&z(JYF0AbBt))KmY$7vnY*@&o zAtxu-wSWKqNmHkWo}Fk{)zs7^GSzG8kDos^>;L^+{^;GibquGToSY0Y`{m{3+6_z^ zm6erUi!@FhYULJn0OiU_Q>HB0l6l#UQ6xS--ZjvxUTp2!wNuZ{wbnMzzXx`IL}a99 zVq#*suCPlpJAF(9r0Z zGe<_Q`i&vOzW@KKjf{+LY)a)m-Y2VUZa%$RT)*hW1w}F4s3TpX+9yt&P*7H8b`1nI zaSp_u&eAR}E@tDGJJThqy`%20)xm=YOI}^+v@CvhVMC&G+Sys54{h86EA#jLRC9H8 z-Sg#=_m%bW^H&BhPs+;bnmqZk>}$}z-iF2Q{f{0zXz1_fXE=E9AVbW~qSh~8N|cn9 zJNx?59vo;~0~vhZJ&jZGNbSYqGR;*1zS28)AH9D+9u%^RE@n)r`}^yqo65I$cUMa@ zGK!?9r@ID>etuTqQ%9L^z!rT+f?(jDD~l?)~@E} z=0|VdtkH?u;-RUj8Tjebr*{!ymkaon%gZOtnX?AGb$9XN#Yw5DOLgM*M4UW%a^)fp zdEe{nBvsa;8VdF$51?~el+bF5ExwYB@|t6Dd2-UL~}q@gv{ zD{@0ZV^B!Q5{<5+>qd$#JNTtlcdmBM$|_RN3l}|kS2r>;@>CNmw@9zsVhd|)ZR@f( zD<+l6ySDSmUQ$i9E`O)PAMB_SOFW^yRYu=R3vcPjcP z)B{=-dxq~1|EI$2i!J-E9W#3*JD;UN8&ggS zF|3Q*3(7*CzP?Yt->-lExZi%+h7A)|hp&HiZhz5Ju9GKE?sCyFGn+PJ#tfs`GU+KP z3od5NFwd7eJxy0xS=o8J?}Y|N=66wQEdt-2?*%BH%DA~Hb*?wdt4Fq>M{eDk)zHB3 z=kMRj*K4<1RDM!`kI(66trEejv9FfuZN_9Xpz9si%nE4-lo%SHD+f4|+%Fp;vc zwbj(oNqKi?r_U^t&PkIdS%U)nuR(y^*85eZH%?4cX1uW}wR@hjps1mNfr5fUL*Qb! z(BE5|nwlg`vqbp#_&{eM?AWm*;iSe1C(SL9S|S)n%R&Q-(c?RjfA9cBI$SS$Yv zRFmA>TV46-q`IU*0>kd|_t(5-oH!I$O+T}9wR2N*b7ybw)pChNljQ3E6z)A!udS!| z>}q&?>eW@DHQ(=+@A>hlo8OjAu|?o&+T=CU);jMheI54h*-EEBa~CdD)DBy7;MlRY zPGR*)^X9F)9qr4Z=<-(mxwig~h<`ZH3aA92VoyZ@31xaehURd)e9|mdChf^^xIX(UO zzrVjF&2l81+jt)C{eExpjvWz@pkeWR*CO!L$ka6T>8YtTzu#^LrMEAyulLWGF@x*% z-5rI^Z{ECl#UH?-=(1B~r?AtVt4lm5Th#rrIC=8qmdwj->-YcrwQjZg_Po1Zlhu49 z_SIOnwzl#c1}U}(D4d)tut@0E)@*kV501;r{nLMadHLnd&B+rcOei|Px8$WzW@e_( zd^=flb91xYTPo3+9EwMrRY018u4I|Y*L+}9_n+so`s$^f#p!PSa=tDuE;B!$n`;dk ztW{N2J#zfGaowK^8ylMxt}bXWz6-Q{XR%xFqU`JIWUR}4;umr&9#M9jWUY9lw({4@<(B2|WVFNAsi>*7 z&9kk(v?+f&7GZ{=T*P61jSH9goJL? zmL0EuMy(Zd4FoMKl#`S5nP)SzrKP1^WUYXcjTA^vNN3sGTcAmuS65g6*gtjABBieG z?#R7Wrl5xLyE{8YL`7{|G+G4~y_;NqnoDujnWf&-4UCLdT@lmJn#$!GsG*_p;4r`a zkz>c$+PCrXD|#umthfy}cxJb_{yj6M^n`?ljT;SHTU#}{Kn?D*vrHGq?k+2NaiNh< z*6PBx+-NnQ84fNB&m0zZ>R?v|1^cO(9R-ftmxIP+&z(C5YV>W(z5VFv)7I_V&3$K^ z9X)Ws;rv7{MVGr^)m|E1hl2fWTbD0akBf`jl6jepo143~UA0A^UnVj@v1Nr28ylNW z{JuGw!OK4Uc-(I<)OmNJsQZk|DSX$R*5CbXsMxYX50pNS)c*Rd{G!@ctwrGKQHP1s z?YI=P8YU{c3k6CXY%vx7BHQ)D`$u2FHNP0`_iSQ|%5JJCqj#7vyHNN<-W031tPtF@ zckfP-LQZ383#6-U7w>cbEUH@uh2f_IWsy9fAKkU6!xc_n=1h70Ldzy#LWN|AEjPi zK3wW%a~?eTu0C^PKHIk5oRhw%pDN$)fAKG<{MWfRtKSQh3hAkC$o{9dsCjYV%#|-} zmKirc<5SX9T6yR3*PbQzdMztv{P9<8SrNSL?7d?LpR%9yIi(Z5fPwA%wXh4@9iOB= z360FDpFVNI!A;AjU3@%MPRC+@!?|Ru;!nRP$8hfsd-w56%a`B#!bJbJm z_u-r2vw!B_Q!>Vz4IlsQet)^#?Wy})u3~P>FJ~8O^}Vb2W1q8Z$qtr&JGmKAGmW17 zRjiTtsTg>C@hRK9+ZVp=TNixyyJE|VN>Ez7VkuR9mU;Wer)PDS99a_LX!6v1@4T*w zAKw}$zbpAu7y7-HLorKkCMY_-uA8^()ViO$JFKPKor~G;RPT6ws_Rp?#pcgE>euEk z$b7zxXO_7~cvb4kexX@- zxYoQA$rIniZkm4ML&Oy+)41k65+?Zq%fgn$Sk|$H*>g?Rb60CjM>o~}iFx&lI z&-d5Lna7*!{VSX38*Etj)Z-U-Y`ux{beDuU35d%m7MdmODSH2w(R*a^=*NvzN^`ryY{IXYt_}^WAoV zMc*bJ-{jO0^|pPc{ex$(-QHiXO3q64y1g*>rOC3%>-JoJ;djQ;Zn=+aZsWAK#oInV znWJ9#_F2~{m)7$v@@I0+Xq?gA^FBhvHSzVDwIy=5XI}doz1S@L^1GL1U+UJHZ+y6s zWj*)4=VslXx;uFLk8WG2tF@KOT;BZG{;Tp0Lb;r~#owiE`JeG7_O0P|#q0JqoOhXDofVwA=+LE$lb$+H zyZra*yOR2y*0yTjHTK@SX|ZYY=EzH_Cv(3F{qxxIUs}-VN*1Vq%38Zb+w;KNleg_9 zuW0XP_qujghhbCjrqw3%uIvbDxb60T&73s`W~OS!hMV_fiB=VU?Os!zDf|2Hci|tQ z3VY*UpACL*v$XsFU+(wGR}Z{8*d*?BMe3S?^LxdyPMuH z>HVvkSS$aY`M`k%C0qOh^YXv@#oXGF@HWTy#y115BCe9-m-ClQo7w=A7y?ouHJI5r2y@kK&+&SEs%6`J* zjDo%K{1uM_-^|(ldEa|yj=c@BPAi3^Ssp zuhZ$ZmOkG3RsDp=i4_KN%3B4U#d%frmHWR=zCL;449Bz}wdi{{oKD}<{U{f7tIW-b zE%0EV$P*C(L6L>4>z6nAlK_w(L@`$`V$B9xwy;~XQS;$#E{f%lxtbuE#YX$G8LmOL_AKJHF ze)}^|Vz%VwkMaL++n>96d1j=08e_o^`*zMhS`+qsxs)5krZ3UcJ)N6jXXeft>u=GM z7EhMkmtyhOeare1pY-ziPp|vescI^5cK;G9k-0kY-{xP*OU#tiRM`Hj|2aJ;|NfPE z>ESlf?>^oaq6@D=e`+hJ8hj-H zGTMnl5s7j0xgSTMaz%A@vF0P8G_$l{e6D|7XD?S(*LDB4Qy5gggOasr+s! zkj0bT;l;xQLDOzM$WkBNCty&>ikXj&FiSGYO4> zQmp3PWsLTY+b=!`4#g~O*FXjX1A`sUlbx=JSXx>xS+?xnw~3P`IaO6zSz20(h=@#> zH_tCGPOtK3<&ve#C(fVlpBJacAo)^q*Zj}Xn~XMTXliN}`y{VCc;Ui^H-_B2ZfliAtXXU?A8xOJ=U_ru4IEvwq^v9+i1)A{#p0!}Xu zo|vfIsnL^gXci|Z4peL0Ewa_u?R`6a+X~rBmoImAcPs1Z^&L6la(Dj5!i`7Nj|j`l z`@cFH!o|&%zdAcFweMJ1^UA53TE?PoS;|+YWre6|g{W!y==gf|&s@zIFnjgN?&;8EqAN?fH)mC4Jib>Eg3YO+6hoecS#+cbtkY z6`k-qy(}U+W?665*H?+z`QCqjea*bH!qBhJ&m`xDfV;bUOiWBcQPH8JM_Z4}Rlf;* zk&-)UqBUszTTDw&Pfbm&?Zu0XMT-~jtoo`Icrnn$&0VP2wC&54lDj*zj7`n@ZzeoU zXndMHEuv)0g4Y+;&A2@)IWM_y=Gw@Y^RK1NxH%&+V11gp!_UZ{PnJEMG<%x2UliZY z%$+Is&!}lzc3=4svhHYH^8b~8Q&iF>Or5oKb^02!WrdfI?bx~N;+d_I>(;H?#cytI z?&ItGwIV#!eG=GVY}%TdifU?Y+~Rr#4-c`Pn{R)9XZQ0RH7|`=f3h;nh@2I;-?Mg& zbae9dvwhE!XD44j!k3+%y+(fXu_K3E&X)&n_0jP$*l2KY+r-?~=VGEF6K78FbaP@e zPtKcEq}%#rMv9V?lE=+iPEkox0am}RugFy1W3xBo?kdxH^X7q$1>ooBzmip2dOv9D zB=ETZ3Plmuho`1$&#|kWb$PjevDOoj{qz6My))<1@tNOv-xlUP>{z^BdER83!mO7q zvBK78a?gI1sXV(hX!VBdr1n^0>zLg;?}f!piFfhx;qnypRQWg2o%Kf?+n{|)#&>~H;KM7IIG0cJtKk23O;%q|OU+ViW{bU0 zhrRgum&VI)ZY!3yl(dvs*^_a1ndx1t^25iEx5w?Rdidten&Z1QyuoqsNb%&!lMjD< ze9XeeR`lV5qmy5Iz#83SJC_>k$;Q0+A6GB6HNT}e`}u7#<&O5#0jVpzf8DaZ%cmh5 zxmU7w^6Q7!J9+z4>u=PBt&8&vbQD~7HumM^&HSK+$>HncOfoJwv*+$wTNeG`aBX!p$+*W59v?oh_GRRqn!6iTK3 zf`8LL&$(UNw<0(&A|lEl?TQ4bZ(LJTvnu)iUSCjs0ycl${XKq<`+C2Ex_8HVd6Jxx zRCey>UK@6LTkh?Momr~v{4z(bT$-}&@a;>PKI&5C6XvG|hW+X-ajN4`G*Ot+a+-5X z#?_`yeV>#|OFVDfycv2m72N0G`P9}m%l%$JVfxLRH$fvxHShjgek}aB<=ypJeaY6B zesx}xSebfk@`@WOgNWe%Rb`sGJ2 z{u=e4MP=V3nf}Xq+%C(V`R7v#Xp6|+y?<@D|NL<6DVJi4z}A3-1ciHV_sp~_wt9PP z>*Ki-%3pl1viE%BGIdpYNPpSkTZYC3YRUU$ybqjXm}Q#Zwtv6<(W6JdUkfumssXCR zvyL7;y65}7>Mw6^Pp>-nbw+KDne#n)rTk8TtEUbeXz)w6GJJkTW|z;aLvOaHU;f^? zPeoNVbQdqE4)0&{WJOAu{*ANMy7!bnpFg9OZLX@;I^XU`XQkX{kG=DvR$7^_zn60U zknNdV!|7k7Uxtcx@303g*!#==<@x0Ec8M?8ohvghdtb>9i@%@P>E3ZG=InuTcB8k` zGh=#ob}XN+zW?vH=%uf`iqu;wQdrOMD13PBuxaw<)m!f7Zr)y4@YB&~vetZqy!o5w z9$3$G((Bx+OA$x*c6W3vh;1*6`Eay-3Il_9A?S{P9aW7l_xl~s@0uU?NMrrv3A3l2 z{&Vf~&HqRLv1jdGb?f4G_l3bv9z8uI-F|Pu#C&Bo9*G0HN?&WOQ9e0W;L$|W((55r zelH`RzV(B2h2QP;d!M;?wT)O|{-ei_KfZq2e#`Uhm;1{ezgf9|`oa4fCof?5zTcm>?=0^?|ufgky|>2Z|p379(#WJ6F-iR9>&|v*H0@m{Whz5U2yGyi`7|KS-*#bufA%ObHw1n0qyAcgI7*%nYC!moKIGqX=Z7A>Oa>0 zI`<;_Wf`@Q4;($cH0UeutEqgkQ!>1NylkFtvF(?H*48eNT=sv})P zKK%3TX1^MOkXzwLi-qOafI(7*uNvzuW8ntJ^B`1s|`>F2rP@4S2e{`}3& z>6MQ!NY9A4*5fMt{j%qe<+}NKimGZ$xfZRQDYw<{;)nh=;c~S;PksMKFD5R2Iu8`j zi`=+x%N^@0FaPjpm3Zcjb&A%{-=_S&ef#2->lf-Os>Akf;0pE*PLrSQ>lnru&-=G) zZGK+rvGvAUq5r2f$IC4_y+iLy%~tjMeZQwQ%w*gmd4r9anc1(de$r%Vf1|>;EW-FYt~5ER(+AZ|9s}`*{3h{Prp>0GKRym+KZ*r&%8a8X}p2gQqwZ!{+VSi(TCo(?v{LOFI630 zedg^M$$cdjUY&;y>hm*wIDAIF;B)EX@7a3|w;C;dd*M=fw_Qi@Y`?;H&x?D0XUXW@ z(_3+66<6}cqD@I}rYEmV59yzvtGH|1C!MnY%F_0EUOc3VB>#d(%O?;+}``o zOG-+viQ7Bt-QC@fA3Zws<>h5ge*Wuox98utyA*W!#m6H4+SXOCRkl>F;^o%YU2^~R zUyEIz>XnVt%jPBip7Yj4wqNVB*NzQu6LYF^h5hxbW*rR|zx=8*ET_KOI`Q^liMRGq z)A#7^+{N-gq+U;UM@XLabpF_9w>RFu^n2?)on4zc9?J7RFFBC8PwtMbEN|J!F4 zuIHaPch1?m|9>m^zoq5fhCvSdzn+CL znEBmZFn7J4&u+WNTY6lo z!X%;aO48l?_jziBHU1v0c(UL9NA30Z5;G-2cm31V3ZD9|I5{|ZLCKOQ$6BRR)YCUF zIX&t2E0sSJ9sc~Rcy#{w<;c{p){(KTiD2``z{F-!|S%?F$#-Zs_gieLFAD z{ll(=c@>{I&*Yxvp z_x#+^`+7_UweL$nHF#qxd&Zr8hP}5;lMe1=W!Uj^*Txlpm7KP(so;(c&j;}5yuD9*(?9;elGd@>5biSyEXQ& zI9A#<>wC_*iMj5lbSHy`?2=RO>clcmpIh{Nt(TFI(G8ni+!B>&7<&29vVT=^{&xE}dAQ+4!LJU`h49+3zY`H{Q9Fou%)z zy{+iUes>9jbcV#+hh}Ul?OONe=vDnq@0OOn`F?->Z`Z$9to>g;lmB;lnr^g&X;z5n z-Pp4Q+84WfdY=6Ke*gG^0}RH}#(6P24koXauYU0{WZUEq=jU&(etF=|+POQ73su}z zFQ-S%H#)w1?}grYyI-x``{NFzFY4F(8M^ZSb;(^<4Yl;NYP%2JwTfxTOF47!?7}l! zwO-8#c@p?qx4+!&$NM%o^|b_aZ_9P44q=KW`CzBC_Gl%2boA`yJEG z_xso$u5w{@58YLldS_OI{66J5^VWacv-XZh=&$=3Q8Bk_x5m#?Pq#mLcD4D>k8|Y< zKNnukofjRbvwcU!&Ij+F`(4=XxWo2cTk~$`nXF9fVq+`Kzr^;fw>sBYFaKcs`TE0m zuFaYEKfG@C-Cd=R+vV$4Z2G<0Z{fm)HD4~ePqn&!;snRqFz?&jat+PR)fXB#>UYXI zT$*}mJAcR(&mZYbb^B#!)Nh+C6~3$gp1u2T#L(uQs;4`n+gERyXnlR{isT}5)3w1N zcRv|@z7R3x|7Xx-$D(6dSM~`WOFPz*%YFFXRk{0~`y^&d376V*-7ww=^7@o)&7MCs zDqng2o3%!)jQRU7|LgK4D@x`~2>P>sr@NQu%jc@Mgy!6~NKVQ;G_SQim^gTwnlCGm%VT8f4!NKlOugI>{Z=X*QmSC^=rN}$(R41 z@pbZb{X_ND ze!cT$54-E)3%*{tYOPkTr>EB=W4S5l`-1#s%a)bti^_k0a&m51OiZ`*ZpD%mWV9?b|3pxE>~-labXps*f%kgw|BPq+}!8?y|CtN;V0wIC(oZ@XPs4^|1aVG8mTik zc9ig?{{8h8bZ?HMqvMgIM>m$d42s&4!FXVSW3$E?{U>MojH@~~79Kyg%kR+Ib4Tyr zm8#XcHqGwWo~;!R-^-~Mw)H$w>WsKtWdr$1^|IYa0*}T8CACK7IF3fz~v~{y- z;_bs4Cw|`l*YMK((#i9umf!tQ#*p#Z+gd?YRdrYKzk8Bhe0^$9qqt({y$#B1fBpWs zxVZTA-&RgEs=BznZ~w1e{^GzUX3+4?{F(k& zK5CSm)7>rkVeY!vkbooW=l|>ST6^eysQo?5zkGe~`eJNLw;uYj%DJq_O#EAH!i@`w zPp6$eF;`%b+iT;!{BBvwO#8W1^lkgq=FT#xIG8Xe%3|s?|JR2%UOnDnEq%OUJNt$7 zD*3vKzwCZ%tPfsrbuRo|x;y#r)8^^Il{!KVyuoG@>Xheh-hED}T1~9s>}9jy^>Ma; z;=Uffac2hee>(>^CpKOiDT9oA3bSnQ@o3DQlbe<5^mB2El$I;MP7}L3 z@4egciL%;z&inthdwTxFrJu&q|F=&3@}s1St1G}u#4p$8=*r&ZFMj2OAJdU8z8>*! z-RT{A+jD<18oX&vOi@k$=(wEUz{KcCdK15$aP7PI(jm@wtDae*l*Z|d74Ti49rGjHPj-XjMNH0b8%EKGert%CofTl50eUoIvAwo1) z_D_13P)X~tx6;f@W!I_6&idf~Kt9zfX65yuP75JMhqR=$owqygyqckL^i;0w@B7QO8E?F| z2b?;8DS!Ek=lu1@Zk?L-e184DJ4MV-->=*w;Pm1o@*wNda}kDFtmpqdzxnH~}| z>r%77vuaKpQ@gY&)H~`+{=psVkB1g7*{-dr zm-v3tUY7OT%QV(=tapsF*jAwO=Xk*`+xxu9#>r;?vKPOrN%v1rlby+W;NgFneQTME zvz6c9*%Gr)_P6fIizj*Fjbx^Fc1=CMR%iR%&AZQkdab^HlE}ms*II6?zE>XqBmWu( zNl$&ri9X)r^ zgVKZBOS|%N8-p%x?dIyT*!fSzGSiZ!R(#28k+0Q@<9EugcvVt%JrdMVbZ|H@cbRt% zXy7tX*`p}DRAgz%7X42dDv$R|*57}7<=7KFU0vR-QN1%~-mJa)|LOF235yJc+GXDp zA5=51wy+9}*J3TVmRD<=Q4j)rH#WY`0RAvOeqV zzwS%r+fy93#g0A8oGodX!xCTf@o4P%sq-TgTUNMv3VJ?$xBjdDz2?0$H+(zjrh8S& zR7+U&o9|o6pEo_;%)VtGP~E>uJg$P#+uM6y{m05j=Z}28v+B9l-l&i}^0j3?SyjtD zq8Fqd-)#-*jT@((c`(`E?%;(F@3eQR?i6Slao&NR%-xw~uX z!^7?2tLA~%p}SblGS5GE^1gIEoWN%+5!(;2QIc@v;j{U-**&W4}9n8YQ!u$Sw@;-FvP)5$h8TH@)n@g+Z zYm{$}SCk9MtJ3>zck}q9KJO0huH?txr7guu-pmuSo&Xt}o-lp7cq2RW&0VG1lFyAI zZslrfA76VVw?g+wvq!J*)8plJDT%XNWqv=C$lw3fEd1Y&Lo*DM-(_nxHw1WM_T)vE=wi zsW+{+V%EO@;G(;5iiV3^_md;Lgy-C>WLwVWx5w^rX~1xh0t(&jlLS{QLQw zg`NGiHBVrlZuGVn)-6)n78Wzk+x_o}-CdTr!f46a2WBO5mnF^P&G+sNh`W+=GVtUa zJ`3L$&jNxT1--bilG%9K*@Q1IE>68yup6{EO|eCQUrSZB)pxep#6W%$}AMX5kUh%M1Jmc!B(9n3lN#Lml zEzk`WpaFol$8%$TKIRhJCbs3xb)oZ&^R~RXE40;3?(Vfx)uXCM=JY-MbMJG^pQlQl zf}J;R+%(8|pm3~T+W-E(TJ1Zl-!XbX2D7KXc#%=|{@&Sjw!1BgesFXxdhqCF)9m@u z>tbUvpIzn=<`%AP*Ieb`<-;|dKVI!o0n0>prITJ~7j{??muWuKtfSNRO1y%ii> zpSe2!+s?~Qyxw6a=lkVj#-_`gHg4Q}aDBV{-l}hpI@RZaE};Cy;Sct@ll-H{k3q(M zczBpaj#I@-Ny;^FL;1$zOXqUyy^YDZvQCiqw%L&@r$X*OwA<9h-R-$!%7L#RUdQn7 z_N%F|G1t4lAR@Xaw${3)w&vy4&Ek8jz8<=Kd2-d)FLwKbz&0#WJ8|aBlkfNI)lE#M zOt>K6*E;9XsioQsGooidzH*53__RKYZ3Rmj-I?XG|4K+z_s(0q&f05>>XM^NC;o6( z>YOg@`A2!;lhY^f{CLdP`m=RL_L+~LzAXxF_k%6VS{1r_)(qtT;|Dnum1V;daAySk~(R4GNXHogp}l^BU?G+=EZHvyDH`H?|*Acrt|4(y4iHSe)y$Ex-%W)F`E@$rk|o7p)tMI~jz%rDpHcg{>vOPe%z zitnvn6Qh4;E?h0ldC;+XwRYaRydArCU0USIZDV8e;ll@qwd<~G&jOWg9Ez{F)`ofK z_6x3SYuDw;RVENw>Q?8KzxrkPtM zpKW_K**LJ*{f4JAKa< zo1$QE`}3Zs70p-Y?0zQp%wo62<0mg%l&8CaZYOy32y_}%VQJ~nt5-#hjW=ukoORs+ zH1E+O&_8L>BBe=_CUx}os;a8CUb%9`NM2J@^N!D6Pp_=Z$hbH;)}O2?%hK(>gkPMQ zdB(ud@Zh0KOuakVtgWrzo%356fxm_xq7c{Kt z#G$CQe8GZ-J9lD?jEyxlG(g9+H_VwM!^O>gdd}>L6CFK0J!dA1iHS)_O1}J;mypn~ zb*m{u)>bKFW8*bz)+nqgv;{=p8^ixqI<2yD+;C3@Npv|~xp + + + + + + + + + + + + +
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 zcmeAS@N?(olHy`uVBq!ia0y~yVEWC#z?jUz#=yX^IOfP-1_lPUByV>YhW{YAVDIwD z3=9eko-U3d6?5L)t&9o1de8pD`9!;d2Ib~68c7RgHZf~WYkDLx<3*@ov4-i_Un+|N zU9x^%(bavaHFf5#oLL>JPaVRZO>tbf^yz^IXI3;co-tZ;U;f{X{^P|nL~JU4?1^9h zdDok**FW5~ep2_{E_=PMuoH)XprD}O#B&!fmod12z!j?*Z-hZ&FE!MDg9Kcxx=z}G z1Sa++ZSn_+ELlvclD9z>6&5K*GfzGL^z`&CzV42WjuozkH3JGbs9#_r#_t)3vrAt+LczF}k)7vjDc2EBO z?Je)NyZh_y*?1%vf`fx+%$&Kf{{O!pZ#JJ_@^^mix0$z`_kR5NQL8p3Jw5s4B-I!9 z_FDV+`X(kNG5Pz0LQlcoK0f!%>+9>4jf{kBtG~6(oGB^7#VS|(C9tKf?a}T0{YTeE zZx;|0WE2+{pIQb|;iJ~Pckkc5^L3PzlrGgadAKiGzMNZJT%6Hi#_ZYM^XvabiinAI zU5~GiWe{LE^5f%U*U-?Z0!|&p&(C>2WjM0HvDqapZP|{RpPN2>`!?-VeAP?Usf9hg zy-RC%m%W{I>i@6T`iZ%@x(poZ>gp3`&C=3~+oN&k&K(wphp%2~85tWd{gr-hj-ae{ znFvG6ym|9FIy(g`D=QZ*Sm3ZGW@iwCz-;q;F(;0R6DKYddVRnC|6hi48iIm?wr?uZ z=a(E?6S;Z9#EFayGb{>~+W2I-yuH2md^)8apf#1DA$E7!!@u9}U%&D1>Gb%v$&-bx zN?)~z$JYdE_4oC;Wo2b8etdqOtu!ku>!lxZa&iU5#m*ic91IU$U0q%AuvI+dRs8-s zSq1?Sk(Lb`3~t=Gap2S`F72>24*To=zI&(xO4)zq)qXD#-n4mh;g=VInY**Eue)@< zs;a7>xOnom*R#(RKR>*!{9jCTbhmunkA(+6Zr-$M!kjrexz<)zUiQ|OmY(*mmll^AAVqg)c5B-q}+r zygF>HQ(~ebL&BdQAAijM|5LvAZTR}Q-Zg7sj7{Q&5~^=PoDJf_CEaa@$rZ6-{0@q42qSvojx3jTVz=Ry*0J8 z4(%#^{o!u;eMMVa*{4sRzPPZEx$677-t>8uY{AQX8hzB9D=I9Kl9EDh)%|$b{^Q5v z{*T}9*E_3CUg+L07qKCMabE4W$f;FoYHBOitYImrE}j42K|<#JsoLQi(#}eO%Ape{ zPBhG$C->&wS0y=SfB=%N)4SK1quh0Mg@B8|V`S-8VZ3@Pn+RQ^{gZ9@^u{Y^710$;*T#ZbY8J;9UFtjRIe}Z@5i48 zmz!7nU%wGHT~z%1oQ9@mB0e0@x~m9}Rjdf4;B(-+knWOK!X4TZfpKIoq5Sl$0)I?%C|0B_u37amth>Zw)-! zQ`6Fto}Zh0i?6w(!{hnKb9R{~1rHqVl`ak2oP3s4YbGM4Q=j7<@*}GS9`st+=e)H|tZmj@SA#W2m`Csu76ciM2I&k{5cebI7 zbs5jXg$pGti&Q>+`c&}mPvzRU3IRdEz}rWzR&>ZY6&4l>I3-+M3vy zWf3keE?16yIA?d!d4=A_?c26pD^dXoO*6cyUd3_s%9RXj79}O6Dh`92%gwYcEF!8t zgs4q^*r`5m!Z%rq0tTa*JiE)^Pdjz!@Zr!`;o;%T44*!I5)cz>J2%(*@QD)~+qP|+ zVUWn=H_v9JQCw-MX;N~sVbT$f8@F#e>rG#rejZf5I_Bo)F8)~e^XYW0$EQ<_YJNOy z*SfuO<;od0l}2mUu3ft6&!0bP$0g?ASk%i+2`BJvt9lD$A5i&&BzeFJ@2Ug{y$1@e}6iy zzx0#U>uYNduZ!I+ARxe?$iltuFqmUq&L=4;Im5bKZ<TQs8gyYVR zLS`q9GxP1`oj7`=&G{G%^6%N`L~LM?l$7-F@@m?*&yK;w^4x<52~+=_I(=HP#UUt2 zh~dG{=kt&6e!s8#`@L#@R#sMv@^>=3N?*4XK0fyF#l^+n|J-lt=;)C1t8tiqYl~*^ zvV@P1j^5am%Dv2QF4u#yrr&S3-(Qq{ecg|DyWa=I#PAds7c(3XkFRNzG*0t)yLi!} zf~TiMbMEdE^|$+}!mz--U+%`fT5GHFcRj7#;*WlOd>m9;UtjOy>gsu$TU_tR`~Cm> zX3UV#iQgw9CMG8JSVu?a$P~@s3u~jzRa8_wJpbHPQdWNW{yjf~gh4{Xm$!$F&)ZyH zWAVE9!2!lEFD^1GyY(;>{66=8mT5MqKr+1*CJm}!xh1)|xuV^DLZ<)q!#%&@SqiccriH&%bod-Ckrp?mk_?$`a+W#f@( z2wd!z`0>%vAJ1mz8x%kDd71Y^JibP8Ykz-#`@DH_O-)T9zqW4Q?%pqFTkz-zXG&Vy zp_?})9UUFBzA_v*eVRMI?x*UxIhM@v@$p;#u3WjY;?>IK58l1&D}8;>Fo~!1qB7S%7RknY{O)> zM~@$G%)M=<6S;{cdRxxH9!X{4&*=vdJTDqHjo3=Z8ADWmM(cZA>mPs5x!GBy8x zKCk$E)_iH%v17*qw5DokYC2X{TJrEn{VSEXF4O6gv+bHOL!zav?a;AfY(_JAKso%; z(e9;7zWA}RvHf_te11Y^W~XB_+rypD=QS^1u3l49GpnZNZ0}}8qnSLHm-`?8^73+E zXlSc*JD;MJm6WiW&jIWAdzw|fr`;*Ozjm#2J0E|_oZ062t5p8Cwzg_$XdKv@eZAoC zuhJ*apDU}Yv#a~hbBT+S`}FBkKxE{~OYLjd>b`mN=E0jcYd+S`oiisPKY#t!e4cOb z@AvoH|C9Lt^L+h-hYuG{mFVbLk(;o|KX=~5iGof}P8$*rv+2a{5_x>Aw>d_Soxx|m zoh&COXT-)N)}o>!Py(5{4dgcU`87?+$NLVRKkuJ(|%UA3r+UJ>g`EMcEsP zH*enxiis@?TEAz{o_jaWoIU&S$rBa^P~q_4@#E$P4-#bT>ty)&_=zYlw-7ZP>7(VfE_l$+tzs#g)y? z#Vac-chvp0GBP$Ue0$6EW&QsjkNICFJ@hLTXVBBrD<~~(UA=n>RgvbFB^@ zJjhs8rMutH$;*rD(W6HnzFv<{zP2VZVrS9PFV$WyE^oOf*H&qOd@OC2bKu$8*@Yh- zIIgs@u#kB6?AeVymBvcS%2$P!hprBL_~GH<8K&7{pw<$PlnIBascA=d_u=gId)xf& z|Au%i`1|+o$Is{Olh4jFt@w5`Juxk*>H4eJ9QyQT^Z7?0tuu|&KfGAnziR95!pCg)DjxG5IeuJO zS(({usnFi)@5j3J_jNox-0uDL)923{b8Z^FxxJl#=gyrQHf=gI!!S7^KfnL#>TqU; zU*Fz#cXV*Hw6(43*;w{AibuvG;4NcZd3kxvjsizrHQ)blW(lR&)a*I7^LuA!r-Vg; zg5O-LR$ghd2PY;f?TY?;Nio@o14>G*wV+PmoE>VJX!M5DdEkHjX`^NOG-#MEDU(?bb7p7MMcHreYK~~pKo8gcI}q8Z{PMF zIpQ*7#tf4P{vEZAGV=2L;^N|4>&oBWY6UfTt8Z>ft$5sP9`fqSUTvp^0SmTl2}!*_ z%QRa+P;jB)vuD@Tl`e()eDma;T$`2a^X+}Ct1IiK&6`8@*k0MCpP!feL2|P7%h#_D zpF78=9k%Aco12?o*P0)h+b3_YXIo>Je{WCK@*S$Gs)`yK9&ejKJ#7wNUe}bAC0}x9 zs{Q--Z-!Z})Uz`)7YEgSdgA%z-CgO&$9kP>YHAjLG}~?`I8hJOZe7&g(b?Hq!u~(+ z>eZ_r9v%mR{cW#i*qfS~g2MdI&(DIw!p)}H*F5w~vO6|zG!)@tHOsrhVQ+80RWsMc z<;u1#e~biNT$YHkv9W1rYG!`ovUlR(P%QZNCX$7XE$PGr#UFpa-QuWuuQ;^W@3Gpc>z<_LoQJ-@j}2?oT?(rJ}0(uwB0Hz@b*| zjg_CDiP>-|DP?K;fE;w<>gw=|tHbrbyuUAh@7}!+-@du6kK4N-KqIO|#@5yr)c&dc z_2roR35qk!^W{Jp zCGYMo+X)*xJ62@Qcr$s{mMtbrmM_na5ud$6b;{JKnbs_Gzb+8^YsJjY=TcH)BBmSF z;yGE(^hZPf-B0sM^99!V`}+D$p2noAwCGr&shpD1F8##BL=H~QgD)>H|M=(g`Rmc^ z^RzCWooz1tX8nc@6Y_tkDrG5xdKfk~HV+;?JUBfoaP`XSm10}>gvV8~dMy>I{r%1L z{=V9UAzJb$cF5O$5%im9)9G*bbIHA_l{Pju3=HY#=B(U4|I{h3IrjB({WhOGRv+Nm8c-1qaDbO@-Qux9OA!-NA2KYsjJkbi%lgh__Lnzd`+ zSHF?7tx9-&tapZ4Zq(k=YwKdWm(Q!>N(`@)TDd{CPyP1u=g$pWw@%%bY9tAY;m61OSNHz^a@qg!jg84SHYT?p`{C9v*Slni z%9_~SV)}bNFclRQDYQ5o?~{G_`gM0p3k#2wiO1TxP756}Gc_3=+|J+M>Z6wZ!NvrX zOF?dm+Ftv+tfj4uiJ_;rH!v`8;nR)|xwzQfWucyLUIzyUH_w_S_2T8rh1<4?$y${h zI5X4us!;8NZ$h)3I@FGTILI!4#d<0`!>=zdn{}hNne4P@0Oi!Hzg%2)b)B@!ybJ1$ zq@EJtk++lC|L0TpseO|s34w}>dGqGo*qq)!O(`dGSB1})2eG@$c*EDn9i6Hj4(bGe z#wVuh#UA?k`T6WCasK}NP8<~#6%V#vk2`#%Q@HZ`><$lh!<);^d^s9#-HO_p$$55R zfX0KjZ`YP7F4Wa6^eMe{Yh&Q)6)RUdmX(>Ui`m%(s+?55l-f_9KAm~T+%47?77L1> zpL=m-rSP#{>GmyKwp3l;wBGyE?vj^H=J#tRd)f(3>`UI{pF3;kPD{O*9ShzXL`~6? z6E>YIz;u0Gth1jV-=XlHHySqE6+p;oiL+-xHF;5SabReu>ekI$wzNRU=-%Dko&4p+#e&jO z*X4e5FYGK%x3I7r~dr;lXsqRZ0YA` zzMv7E{r~@23keB*cse~k>Fuqp6(5g^8x}wF>5(*Mi;IgZC@5%{I8jhWM&`omaQz-R zTdDK5-(}2lZZrt1`#HqN%iG!6MeHb0wEOu)SjMV^LsL^zLs!=|F;Vf{9Lwg~-`^4s zxA9i|d^+8*^i_z>zaNhaii&nESJkk!v01QuIlHZ`?Two^6aW7D3TkycJ2&^V%lwWN zdj(y}XId04nqg6xwED~3dGqw9cr7g`DiX5)^PyQoPfx7=@7L)PmPJcm+}XL=z|1Tx z+c@uz1*5~1>C?SaEiEk%ojvP&?Cfl5+hx|LkM~Ne_x^m`Z_l+eMz5x(=1Nk|iyNCZZw95Rmbr81 z-q>Bf9^~TX{`1A^>go<1IpXr?*X#9C$G?64EX>X?C$eckkJ=CvE=z zwj;-mb+LbZzI@p-w>vuuon!V?1pfK?eEy>g3!NYBe!ovRdRva*>+9>q^Y{H+R`GJ_ z^doO>Zgwpw*dSq3v7zG2MfcXdd-occn20ch*d04|?9j=Ro?l*H_h(^a>q?(r8>SPx zE9A(LBOUAa{qmBrtBIInTOD?1N8#eD{c9Doudfr`wr!h|nws0&@}7>4TjKW~Eo6T+ zH9XESF;UU(*9&D(|AFDc=5+s>UoV%xxU|%J^6$>Bu0u<`rwd3+cQ06=z_9)KA^qrW zK96I+SBFMLb@AK%V7Pbh9z#Rj-(L?;>+g42AGdcy#zm!76C;50^>)g4qlaI|cPUowstz~35+|KWwkf2cZ?oQ{QKQ%XQ+%Q?$zhZ?( z%+4ZK(CE)(e>=_x-@N;`<=^i+H`hA(;h|Qk>z@5RJuUn9+cP8_Xkhe>I`%OxH7!la z&`@ye)~#9jWoy^0afytaxg|C@xOujD{-bMaqd}=QGE!1YTRSi&rsvhGtUsU6+Xu}( zb;=7gD17wjQK{pmW@c@zt*kn_x}eOTot+&K9ew!pY42Y<-z6s}GfPWLM?^*nn1 zdJG3<7$#r&rmCWHCwO<;_juzP>(-(pMryMMV!DJWx0)EFrNX==zNt z5{!(DppG@D-zjOFW|A2rFE2keS6p12!J(|IZ1Uv4KYmoac=^&(T)ya4<77gBlfVY-|SR=Ix!GoH{x>OIr3hxAR>(4;mmbG^|WGzJiyJkM9;= zV@Jm=4z=H34qUn0YHDbBv7&uT^6@@juW#Dg+JPYig78b^PEV$g<-rRiq$;VMqQLA#UuibX(%2TV?=O!-O_ioC(dF!UVnLTr+spYxf zrkR(Pr0reP+0oH4tDXPFCFbOu9G>Fh;*gM#jGEm~d!@~1-RWPtR8>n$>($(4h0A|O zI`3Jqz#-YF>hG^ohV7I89ecF1?5$M8?eZ(pQMJd@a&mZ<25~YoGlztRI&v)hXk7B* zf>=Mu_*;BxzrQ$K_1(R4_PJnndwcnnD_8oyOaJrZBSS;&?{6#Lg4)Dl`f)nBvuDi$ zC8kT;mM>Q~%e{4FNuidO)`U58)&za~e!pJX+B!O__362aZ}0A|4%)tHlMp+fOhb%b z`0QO*gruytaSE#~*tIL_u`y5n-mhXYv9ZymcA$yNOnFe^xmWYqHzG1p(Z)u`{@)K} zFE1}pquyt>+1f~c`*l%Ux$^SzBKA}metUPf`_ZGMl(e*@gH5a_PM!?({Pz3H%jPXx zOj1%(6buaoL8Cbj4mQs=>07!~HLmihsEV4}qAgoQj`zzS-;#MbAtj|{#tezA6^9NV z-k5$~4m2pBtIHd_+|Sj|k1r`HY1W^Dyke7*oYi4#n|ANEPCGlRwWWmxH1~0=SNg_| zLgl#Xx27)c?!}UBN=izx%lEc6tXP|McBy%*fRd?cC<{9~H#awTrsql9>Tewl4Gfu` zhi`69?~dJFwo;X9<_lCdaoSQUKVP*qKB)l~i1T_GHbS57fAGtaKcVP$1aIyp&ok@s}HLl-Ut z+_-hCYofAymwf%7z*UPg-(355`NM)~_f(XWvfigSeRydRWSV(Ng#k2kFirWQUVeT) zXvE0aIQaI$7jq!} zjwtc>_-4%cTPLggC%wC~^TU@fElvv?oEA2;x3jzT$#h;_9sc;d{r?sLr{vpDo}}1( zK4V-^T-+R^w!!TfGAErsWACu!LKhb z#a{n^a&j^U4^Kd^XIAswR_(Uj~+eh$kFK9E#`SYC?zH3PWk==Z@K5( z+??*bGUU-^fBRO@m?CIY+r3Zb;2g{13G?RhB_}@?lkD&4=(uHe@8)u|*LQXnN9?UK z{q^nb>Pau|zXeTMcXoDqMS6L4UHNv-<-gyrU%w*u)l^PSGBh_o-pVb$A^-lqW%3TT zuY{_zvtR#y_c{+``b^_=gThBHnitvgnXlHl&}QCngU{OSjb2eqTw;y|smN z>(;F^OfrL-zfSQ|J$3r@#r5&~)3$&8`n6SniH(i_o%gy~vu1_8{{4J@JsZQ+;=twW z-W+ITUTX#_@V4rLhSIHyo^XK1^gecrvG8!p{99}0%#mSem^w9d^X;_sbmokiD~%2wJSgCl zaA!v$@3i*zc4ZqI(2ROVZ|~9b_Wye>T)0s6diwO~&K!+PmZ&f|fhau)u5Q#*Kzd zOiTfBaeaS(e>cC;9<-8$Val{=tKP2GUcK7c#l>Y;Kge9?HXcTXKOc|FpUr#y{5dN0}^Zi-?L^l_M=F$>=b7+UqrI)_{g$eSCZp^77VgyMOK+-+{L)?RZ~aTPw|= zU~C+m8y^-X#&F>9;l&q0bE6*Specpdph5n5^W?VW-@o@_%DmlYXPfi0v9aCQS)AVd zHLmKVDyYfBzb&hK<3>XrUERRIz{W?9lD7UWy~ElP;#I4-0s{j<3jmr}xi9RkE+3+{*wgf4yS{uWd2@5K`&-r&7Z;Z$hgmMhs`<^)Q1hF!;;li*9E(DyGqX&+ zckJHHy)#Cyr>Cc7@7}!*+l;o0{nz}go{jIIu3;{nu!*oi9hJkBqUS3-2#IZ0;PC{bEMtKI1n3T4?eSAxM zJGZ&Hd16L}hK{Z-sA50T$jm;gbl<-pkNFML&PXICCMt?>wSujGIz4{YRrlFw=YRb>?u6SGB@>*b0~n~ZYX9b8jn{(X+qqkQj5mvnY?tSJ2mQve#|0);g4qO>mo7L}h=o}HV^?eFieA;P60!c|dG zk?`ciL{JI-@6Ts{4n>n~zJ7kkjL+LJo12@Tcv=(@8ToMIak<0$|9#a5)db>tF%<{g znU5ShcIftP>7t!7Ml*SgX7ViepPzTp@Y~zl?EL)v5)vOS$|xx<`ql-Snccc|Ye!Fy z%l^8*2{}1EH*Q2cdHPh**jPAvdtPgIH}|4Ni!Lnpmj@+*wQoUl5suAl3zLucd3bm@ zfO`CMt=qR|Uw`!M>}=4IlU-$RyI#F|#r9Z3Q}dv5zfIGE1q*)LElWP$r)Xj#qU_!W znpHbD-=04`{rT^A+t0Ofi+{Kh?60V$#dUpMZ1%>w&W?^34-ZGzU0J3ZzfUHA|6eoE zxWmuS&%K`(?Ub>zv-|MzW8>Pjy7#JH>-Od@Uc7i?=4CaT?{|uWcFvtE3z`HnLU$bB`IY`jt}`TKsd z&9yFf%goH|{^|=Vki}=N$bZMYHF~m|FB8LB{k@g%?pWGT!uxoXuacC))r2Mv`@nLhpSkt0iv z{?^ymZwFP|7S`6v=H}Zk=IhCA*|pUC=ks=G`q&yd+bq{9H+OBx$3Xd|L7ksIeX7oS z{rdIq1H$J(wddRYdKW=W!bfvzvo`n1STZqu`1EPl^KE;pzV3Q&@#yj6rR^E1Mv_`u zS|-!t_ti)$Dk=tOP2I31i5pv76l7JmM>Vq;5|*JarbWP zo}Qiy3!T|Pqd@HZa(muC24$CT?V!@#($bQFL0fxu+V->O&ap8>L`UB*m!W3$_+npw zE2z;T4yp-F&GsIHpgGFK#Kf4`c2Iw@732{=KRd$wWlx4h}~Vr$iT$R9D1s^w-;1}fBIDP=KlWu ztV*h&oYMp{i;^l2v_#4GD5%--d|owQRaMoEeYMq_!_Uk#&zIxj;n`69{2bfsy?RKmY#%XJsX&MeP!kYqK`*Du3TM zJ-$vd{oEYTFnesit*>`vRYgU@(JoPox<3}65xd^ELBYWr3m?1r%(IcayQ?&NGqNcNHg}1g%hdb8oNo`+dLlzI^?eySBu;I`;n|GJ9iay3YdeRzujqJ!~FU3MMXsdqN1&H=E&sS*%3JF z9H^CXMP$!r|J+&g=E>cw`=1bQ@@2%foS9L- zL1y0An0(^gxnmz5f+|E%9b5C|qI(6Xx_@whamLJ-7f9X=tpD&mFE8gvV zKC9&1w>LMH*9J_PI<+VdeRa=DT3mejQxA{uw>LI2D=I4T z|9N{9v>1^WZx0NVLh!9wek5Ju=^GU#b?NfuhmRgDdK=*3J{3H9rKGf|tPwP$2kvve ze=}K%pP#>?qT&PZt}9uu?k(_kadBDO4OcK3wr&nq)n%&Q)Q6Ye3b{P~OO(va#O~d@ zH{tZt2`5uPYZ7XIZP|7h)S}syALaS2Rba*1wXV6jZ)?9V4bb>-MA(0eL&z05S&U_D z7S`6;);f#YFMWG^dnxy`UFXluwcaW#CK$MT+0>N#ckalD>%|<{SNnU5Va@J>$H#a< zqdL|-X8L-1d`=uXQCm2;TAALxdw1f*i37>~w#(jLc*m(<=Q(?s&rBvo(DH^Ypi#HK z^>a3_UtIU=rTUFqx00TnnR#P>z5S}ytGCJq&7J~U)4Kt-IL<}aKY5e9tgLP=>+3gf zdY(KFN5)Dk>~q zONFjoyT-xAb*PnF{7Mq20kbjr_>wXt6xlam{^ZJT!L)926c-}_`cdU|q##z-$Nb_cC001dd;{rwsaS|1uUJ1{tS z@mm4Vdhn?JjE3pcryCd>3-8;v@6q-6dQnwXRV7Qy$S<$1dP_)3b~?B7d6pIz6}8Ns zJ$u9E&BD9O-;24rx+w6XGY+SGlZVQq$vPoIiG2a@a zCvInFmt{C>=FHCH^7S#ve{SBqxhQzK-yzT#`Q6>&BI4rRmzVo<+uPeGCM79>oY9Fv%s_~^yO#ZtHZ z=2!&!%(3{$=zeDdXi!MT+smtKhDqk64VyMiI`!(GbnO1RSe=+10VS`lcuGh~aY;){ z8`k})`1pK&{k7Ziche6vFb2*2^QUHG#mA(JOTEPtQ&Lps%$eij;LrdXUUH3$oO!IO zt*y;zWk}b>#qQu0=&L^edcEE|`OeSZvnEVXm>L}&eO>zgQt#=Yrhe|Ee-@=gzUSCUFd+DyPs=t>mU7Gay+1W$)@5ftMT7t$*K}~$nAkW5(i%Ot%^hRlZeti}H zem-wqzI-`oB<{!&mx`*Yu9K71yFpVAPw(dzzY-GMs=lf?&cwjjxOw~b?Ou`R=h;4d z@uDMmxgV&f!pFyVV{5iJgMxuUz|wV)zUPM2(_(M#@0T}DJCl*)!O+m(-+yXbcQ-eK zg0;1@nVFgID`h)7xvHwFEbp3Iw{I_Y@0YVEe8gg8YHF`yL&^Gc^K82pI=4Ui_4W0I%a;$|+L{fTSbOpE<-@(- z?>QF~7?|bX>#;0;W^&^^yL?T9czliE>uYO4OOilqIch!}WUu&oHN5cGm&_x_jy?MF z@-nC~n47EH#wXh~Ws1m*nKKi=zq?!U=i_nEG>GqPGf-O{v}o(k&(E_%r+2K_n`v@0 zTx+V-Y3s5#9dF*`#O$w=4GRm?(9&w^?BqOi_UzSH*Ho=`?%e6icjnTipw!zZPH-^H z+7pwWnyT8>)n)RnborMrC0kSCFMA2q|Nm>Q=0ESwl8v{Up4k*cP1l%jQOH#P@27fB zPR%XJ!~?{T31uy7Fr471zAls7H^czlb|K&sO^S`gnE* z7FJe89UY%r+mnuRy}7x0`N>*dNh6kSG2Kbqrt8P|^-7zAR^IR2xwE6c|M;7mnIa`BJ{$xVg9#S{i)Rf;HFwJ`CE! zAR#S%_|hdIS65dJJ-smRAM0axvppy~boJ7^zSVsjHyTdyQuXrk3W$qa_w7MmF|WMc zpM_N$N{{!+a&U5HZkgBE$hd0tYS6^bg$n^^=2!~r@BMO#x#PpXj9m)5=X2Zm{=HKE z@z#~9??n@%=l|aOLv>d;bmPW0(8i50bxX^gp0#J^+smt}s)9NodNDg1CQTCBx_@

-->Bn(MefZTE3o)4F3N9pdm{JgTzBD zA>rZKf1Lkci_SlI_3GAGJwFh__kE#kt5f0mT2@xphxPxz*B5?z;@Kl>4Vv|D7hsBuiwl~&>j|jIaC=+szyQ&w3Lb74---qnVVX7Xhvc<$_u9c{zrDaEF=g#+)r)Pb6d6_*kGxNlW6Anp9 zN_ug7KwA*J4R`L|4O;XF>I?@43W8Re_ez^T+Il_CxuoPv?fsAS5m!`yfY#32*V}V8O`){)I%N@D1v-tf=J;A`;mo8l19Tyh|nimGm{eq@MX3U-q8q4|f=g$%S z{eKpP+2b5~SzgT1U-mN=oXS+WYmXOf+I(^b4B{qIJoik_7v~+iO zN9?cL>*WKTQ@OOMW81cENl#8pd~{sCe#)y1sofKUFF(7xQ>wqUwRMJd`8tc59~)Mz zT$#D$?cohl{EN=i` zmNg$AU()$`wxD4LC1qvjKfmw)7b`0(dvt25w$|5IVUKrKeqMGe%VxG&ZqUo`KcCMR zXE;**ey@60clXhspPwIna&mI(3->TvM703IG5 z(8SX0vLbnfb;mvzNPt-fJZSBzxL%AyX{o7Bsflum3L#T9z?XaRK^wmE}lD0k^2O;AGhFE#J%hzf9VVd3HB1vNIlytx^C`=Nkf zp!IR<-&fv=h=?SdoTOS%Uf#ZOqv4#nb3tnlYrfq~*U;C`-;j%Yf6!X5yQfZh?fLa; z^^0q3L9-)?sj00SlaGTIxj6g!`fh%G@$kb}S671uJgdIGV)gg;-}Cd?>>VX9CxJSg zi^@PlmfAaa?)>oaI`86Q4;$h(c7i|f$AX7;P6 z_U_pe5E|NQoPN$Db+LQ@vGx1^b?q#E4q9(CYtrOdv$*Q(|EFDF+&)`?$<)+Tz)8W< zQWCTrQbtDR!;{JWkGl2uIb>$8+~%yJq$CTnt@qR^FA-7EgGY|AJbU)+`xW=VK*6}$ zuc4rIDza836XN!LeblYb#Bl1=sU^E!UtfP5v`?v9Twh5+fk8}6Ov1KmOUc2DcdB-2 z=;`@cweR}$^X2mSQnv4A&62uu<;sFxyQJ>d|F=y$H;0p*ot?pUp=tGb6-;SqX`uCCQ;wK#ou^k+ zR(5QO=j1K2Zrxqo-K)#z`=~X~n(&}ybln>Hy-J_*Vp44`!Y&(6+n2knmQ>+35h zEadBU-?VVorCrPY zjRmEoy58O0oozkG%w27=C)$2DenS!?o9(4c~tv zFAr*$fZ$dIwO=kSSEhmH8`m$rW*@(^Xz4cor}I9_2nY&Z-21>pF6;BVnBDvKKC%Rj zw}W<7gNkYwEC(1^q|d*yWa(1ShG0ECy<2>(B_WZKpn5_{DN7u5y1`b(leZ@qTK?R$ z?eGG2jrUITwmJW(2c60ga`pd@kJk@uIJ`kwL!*iN&~e?bkA*Zn7pbZV<}IC?88tt~ zW8;+z@$tr{frpDSFP}2oe&>?WchxgzbjuwDSw(Kjl(|j&`S)P+#>@amL4o_{k0;9= z+nIi@_I~t}-*c+Z?R4s>@>w`F*Y3!E9ti`62Gi_oSBg{??Tgx5W!lWfJ1Ms9pXDcN8Z3`1+P! zUlgD*!?HLnw_P2iy?xG^o8tUiJ32U&l$0{AuakZG@+C;ulBG*QgG4bgF`%j@_tut! zmo80eX6L`OHT(K4KL0Di?$>2gkDd@^&^SBg%x<&w)0B7B*XRG5o%?zFM5*asfr)|) z?~m+hp1N3c_Og7XvV5JJ*Eci1Fj&BR*iJfZ_Cqd3m$j27)+%P5oE~52>E*=*TGSuA ztK{INOPjVi3w^ziys%$zuB30boGwpa<|mUgZ!9LCXRnXro_Ke5%YLCBRX?t8;mJ68 zX2(nyxy~c&7RoZ*-}gLT5}-*x5hm(|a9TPtn&@A}895;xw;M%VMK4fF25UY(GZ+7=mXe9!u; zT%}~?k+=Pa>zCN)?aO2MQoUZ@X2D&Z>|eWD1s=tDZqj$r)vWJc)7$H->#ighnszPC z$J%P`w!6BQG) zCR|QTY?*fa)-2JzRbN3R1UEN#)b_lyhg!Kcb#?c?wgl~O$i964eth`8($`_JJ10+` zeDM3FOP3_f^JM1Dn>SEe?)6B{`nuS~+1J;Jh>2~g`B}89{QbG7r>9qb zK5PE+)>iG^Wp6L(&XF}vBx@Ow=E?sL!n-}brl&%&$QyDerg%!P^H7QVgt|7o3>?|qg7OYQXq-wE(F zOx!5QeMu}tGi`6N>T1KSCPz0fR<}L=b;(cRv)Vo7lWs5Jx_#@{!?102J471{XCHfT z^W@~W+}nzC{u<_~+&ysQPRp{nvoC#Jy6y+t&!m)^>@(Wtw$pPzZ!3ItJve1)O5!@B z;+Ktp~E z_V)ImJps#?t0y1td-~zwVTLz%b_(0u+k+aYQ+m(2ySi@7yu2){_vL)8YWrn=bGxF} zK0B?y|H{$l&z~O$ZFyb3e7WQ9t%bUe4tn(at6OPE1$zg#)f>y4_nVgzcP{1emfj`t zm+r4!ReJxLOS;?UoEJuZ|B8a%N0eomaaZdvS+StQk(>SEn~!OCbatGSzyIeTe{$Ud zh1Cu7lB+Hi`<(whZ+BeXy4gR?CI5U(*t5HG^5j{E&l}gJd7cnrbLC%jdV?O@N4MB+ zr_5D4(d7%SPO7k(*k4ua^4@drOWT#^3|z^qs}F@2);un3tGAU|uNNosOk~sV$#-jh zKAPTh)1)o!-`P`-cFglF2{3VOb>)dPVR@f-?~ccwgSR}hJNdevo%PLsbo1n9NYHor zJQg%nGccI&?d|Q)U$4hco;Y!#c7Iowl8ueb(obRcjV&x@+}&L+z2)Mz+~}^ZE{ob< zC0mbgPdv=l)6>JjcTJwPw6yf-`SbnC?tK%cOktT~t7uX1fMKy)@1q9?n|Hm`dwza? zy@K+?(4F$p+o$zreS3E|di}c1n~m-4?AUUzeA(K;p8omwY|Rr|3MGCTy3A2YX{l|q z*GoH#@n6h!ja*rlmZSE#i0%B@O1}N~>SjdFPJEo>9N;3bmMJlxRY;jtskQXnL zXP38yC!@1-C#TstGaKv2k#qLW<$ZL*=ZwmeW`UaOpCEUv?$_4Wt>)B?s;hdwJZo>B zV#^B6NpHHPObZ{meER%3a#IQ?!^Vvp-`w8rZ(hGACM-rLV8uxPANevzh+0 zLcYD&S5uf>dNIQ!B{elLDynPk+O-FzKVIl?Ec5H5LKI0HeLVkXJK-lH$N^)~HTc2loa5;E?aQf@4>FxQabn6ru_Wd-z>ih zUS3cQYhADOuz+c)YHD`Oy}z$x@2;>r(5b5)Affr;^;-G%Q?9M6`?Wid3kSxB-Y=GM z;s}gZxhZa%bzy;HMMVXu$z~$O`}z6#=UcDG8Q++=EPj98p8x-T@A-Bs`^d3lK~Yg% zVXL1m?zdw)C3iaDAzq zHtUhKE?W?``sRj2=BTyPE?fu@(^>Jaa`)7|@}Oq;jT-?G5gvJY>m03Du3X8MYFexN z=%B_mk;NykCM2ab&73;(V4?K$Q*VETtkGO%uzSNA+3dq*t1jO?7b;g=mi2VL*PrN! z5D9&~+KD}>pp4mNoNEiqS*9hgI{J>_0v4#nM=x-Onvr z1Qu=c*rcB|C1_>JpC2EWEL&z&|F0%&UChZHg^$0ynlfdIfUt1$>ebqhkM$nDc{6hR zVK+xd$EoL@KYQj@RJ18B(#wl0d|iy;3TY`Rufjq@LqkKhOf8o~9ex^)YW%mHoWBUzrcCmCP&B)YLq3?3mP*^(V6h`TH*U zbk5u=={ifvq*AK+@lSKD?&O`@YZ4ePO_b z%mDR&+yB*{@tbkB*tGpy=Gc0Fd+}()hOGxYGLJM}VRcD&3;&@RuM=}`oq&@K z4=B3xuB-^WaqE_lq@<^Z2S=}z=_TvDoE#qq2L??|O&3?!iSy?9IXW`#E`R^*vcLV> zOFNsIns!uu)#?`0RWdi9-Yu?gRQIPMCMJgG)wy}L()s!MppGbL%5l@CO()Krd9wMu zUGlLW$uqOf{qtiFzFxooT&J-5g$xr=FqglJ*%GB|TOIa3+In@BJ!k=Yb2Ib9hYxe^ z@0)w)PK@7NtD_$t9xf~`UHd`Cv{d)eL9O{^pWkQdS4vhcIVNd$b?02OgSiYAoB#G4 z+_*w6a83R9Ya;r>-ojpa_iiov+UzG^@L)D`sNMCKxxZc)beSGK@~-#jkvlB&9S(0* zn`(Nq*y#G>b<7DlNj|3=ruF=&W8rFbdOs((>2^R=jMty}AK7x>#O#>-J!-#u=Et?U zdGEjPY86%3mX!hXPM=m>Jrrk zoh`)xT2>)zU6ulAB>(&OJAcyT$$=UoE^cnGZq2kTR#SHGOS!fta>tGxA5LknH^{uC z5)&JH^27;;g#iYYpHdiNVqz>xUI-{ED)vYkyKP@kdUsc8&Yc|-8=2W(-MYKGT;I;l zjz`W$f}Nc`YHQZfYipx_KI+z=VO9D{cK!ZEwcpzLWVswRr=2aiUF|p9jJMUvF(@c# z>3jBu?c2@k|9oVhYgMYGqtnC2D`imiC1aV-OsC7sd=1Uau5~}JTjzR3w|4p0qnGYY zDh<9?Sa-hCq*5yA-0}}^U*sEZHCtO^eeiN~Va}ryzgE7s-29ejRt)c})mhhE)+k!5 z&TcSgDk-(SRWYH;P_%T@td{*kCw9zfEDwLbYJT>qD!>0_QoifjwsdE%k=WYw=Jm~% zbDV3xh%C7sX`IL#?jOH2BG@M{O3zySoy+R0mmYPB?Xvp2?EB%BD~0X*{!ZL%eJ*H5 ztjE9J8orv^hWpR{x&8nCJ9){Tui?-B#%&jHx+2oLZPVoWN0k@XaGx=S^nG^y{P80o zAmG5$)6=s}`>w7IH_o`Qz-#^9iVzJEE@;2!ezA0>_~jd4?x{Wcb~G?3q$yh4{MVk# z?Y7?E1$J-GkqV!_F!saix68NZ+d8x}%1V5@X|N#Y{qNk*+Y4V!4+b^AiZ(2|`S@%_ z)v3!>-L=8`OEwk#@tX9{@6e@tla?Qvf2jV*+jxQSiS_eOA34NwzHc5&ANP{V?_J+- z>WlBwoip`u*d(dxUYVXN^V;YAxA`m|_xD-oB!5-m%{Fb_?0QAYAKAQ$RrT)al}KJ| zJ?Cuw+V}X${S(h``+3{qr}xde|6<$3c5E+O{(Q&0EiW!Qz29@S@b1&TT#IAo>+6*F z^E-90J58!hbeQzFJ=)8QYu~#4 zO8KTZzdUF)3I8OY$AXLGb`?HmTkPJiW@tF^&z~ATS*s%p9Ge4HhJbdzY&$IM#G&ZI zJ254`qw3hztD)O_)4spEyK(#W=Qq>mFRlFiOvb8&LtlUWExtfdKho5t^1GH(N4sxA zf2BFC zlx58m*nCNE+0`-*P#-yNl`78(@2y9g)%35fx?z9+nUF(UQv+5~mPWJAu(!YhD&I5;>GZg0!2 zDt&%tW^>qT*VR{(a&lrOKjc}GAtk<}-6JzoQ`xQOz`1jM>-YWQGB-D$GiOf3 zt`g0A_wMz`SPJ#J1>a8O0;%rUot>RI_x9`* zQx*Zmbga_JeFFRwK_j0RHYU3_CqGTKE`PV?;*1snCyu~!&rSNTRJKN~-M%4YVSqvL zGarVo?(VOr4j(#H@^p3B+C|~(<96)Ym6Vw1`1;z~$Q=cWOP4M!w|#zQrg3TRuiw94 zpW2>xca?T|QIXM)xTq*8Q0061vT$&4@SM4GIeB@X{`mN~>Z^$qFX(8Q;>gnWNSs@5)ie1|__&^HqK?^4qj|v-9e!mzMj> z_w@9D`eYL)P6Qovwk1k8c6Zs+cKN!5x3{)BLI;OxvkmPLEE!ehp$gNIY~7oEp1c! zc{w9vV^9aO{?Etub?;^vCWDTL`}t(D|A`YP9=y4^`NxkR7xvfJe|vvFe&z2)VXGN= zl2}T1e*EOT+yv*m| zy?b)%>gvb(WKVPJ?*Sd*aPr)_b>Dt0^OFMc_8xk1L+kED^cJ11apFcMiJ@wiW zrJH=b@9NdNJB!up>gv9{y2{PX%`Ih;;Sdrcq8qhk#k7bmQMwFVt&5^=fin1!!~TdLWJox6`-rEa$x8>dr3JE##^78W4 zw);REa8goS3JNyF?OeTDyRNQI#u{L`9v3vL8%+J?dTH?v@=>!uwl!;Xyu7_PZry4ckzDZQMIghYM~@gl z`}DNK)+pH7%{y?wAw)~G_SYBCF6gOt%lrHJ8`d#@^gns>B*WEjOB_Jf^V@rVbDe9R zaY4bx#wKD*hM-h$+t1I>Ki|q;pO~DyID9?MHgcYC@lI)FW#znkdoly!?Be&$nK@Ik ztgK99s#jN6SBd`mU*DgfRG-gsYT0!jEN$fGOt0<1_pVJ;cIWAvI-Qk`Eo_x8s2sXd zKj}@kmH)~Rqq;w!InSB)^>*6f>r(FQD6A^=^788F>^yk&>QvAXXK&uDnY}wMARu7r z=bbxuzH~czdg+oSpyLXfcP(7FP()l@SXel@;LTxv`z3{skBNwjGpAn8K2?6%(9m#U z`1&}|LiSdtgVS`QwM3))aWQu~(@albjn9mif-!lysCUuHqppL)F(;uE+aiL5qaBTATL&|2O-|lN1rI z*1TwcjobP4PfkocGs_fo$SP=qr<(7qmes4ZB_$;_M7Y%a=kYXjcXPM%$ttO+v=}BI z1D)g4-_QU0+S=sD$9j2WECQ~-ySKOcKAu+f8Tp2PBF{n*;JoqnJByzimA|`lTdusQXxCefe{XJX{#w`9(Xk`fdfT>bH6M?PvpsF+ zmtVJGUvo3_=jZ3s|NZ&7Jy-Zt8SAQ7!ELuQ@9(qS|L2qU)-9?ozQGzI60;@lm+utl zx7r%zTUKUv^XAPA6RAy`H*0EYUi=%mud^j;t=sCWum0)$&vVVb`RUUqt(fi00yOe= z-=4j7tErlrTGjuY8 z?Z)j}i-T5f*u44h&6|?q;^K4W%qb`;0-cSQo}T{Z?c4WN{4FZKovuXY@A=3E!eL=y zPo6!y7c4zl-T&EPe)~1gPP7O(-SG_z6Z7=+R8dz4Ed*Q_v-8l_?CTyrUQe=4g|3U= z@0V7k#`^QuY>yeUW+y$)Xr9_EI<0TojN7x4-{tfzovzBK#eOFD%xOEVVx1h_5_@gm zsWVoe;hpqr*P=bb0W*RjNB zzSwNKyh-!K)&1dK!m50Jd={2g7dIq2OYJm0bog*{8?W@EckklDuB{N{l`^SV5^te- za-TrynF|Y@C9O&_wr(*^KPU6-%*s;H_@ zEK&8;?=;_PaCG5B=2dK3A-y3t{#_UQ&h{?CIwJXLM(fnYqGo<(5!TVkX{lk|;a?xj zxIHCTE-^E`@_epHcGt<%XOzvAr_b3U^Xby*f1okYHQA9ees*Wh?JrxjZ<*TnV{09a zmUd2J%i}F5FJHbhym-%^Js&=O0&T+4*VmUa%c;13J$$<7CVg;wrSZ@qr*m_ykKf#! zZdv=wL|iZC!J(tvF+KY`tTw;7tL=8o&BC|B7*wtM4p&$)&2QnyP&G9&f>yvLg@o^Vyib(3G*2E&Nn+=-^?-wr`gcWU|M`sH?jhR5iQz$viwUQCY^SsN=DFe{e)V z$0e7}TIK4Ps9g`PUhmG+-Tm;@-s!IsMSNVC$b{SGT7uO=Y}rU@fDpb;*+C_P&xA7aH^K?t1v*Ma1Rlftr)vfSa-R zc9p%A`u+WV`u~4_Wo$|&2-nYlulgTTx0U_)`tkDAmFrh|+RXEK z^wFhH-uhhIcJqJ<(GAU$nf-s*J390;%&U0Bd24HS`^Lhv{y&2+>dwbfB z9XqUkTX@<0`Skho$unoxd}!6{H)?fi%%7lt?%p|2FUK)c{IbR7w>^LC?kqlZ$m!dg zn}^Ta|37nn{~wsIJ%iWlr5`J5eD^Ri3sWDpH{7%%)i?t=?U0mHezC2g;%eOt- zw3NG}r{~DxPIi5sy2gph?w8g?ZqBmZukst*i=5om-Tm|J_WM8H?S6k_Lm~6hTS0%S zet&$`6`uJs#rvkByUi4zGrk!Y7qjYCZBa-G!VuFh>wez5!fzRO3K2WV{Awr$;pd;0tTICYEZ259BqPoHm7xhXC>c2`MdKq9x| zky>wGUsWTcgD(%8!`1=_eLwcDr#{=}o$cM3WhGL=WxflJzqVDmDE@WUArHrHIo*oF zm)(2HpV>Cu=YDp5@BT-xpPzo9*lbz#<%R85?UVZ|p18QUEV!DLd3#%KZhMit_jEni z)mJCYp1nG}?&Bj@(2@yI=i}?^>+f$In`2pgA!}>K)>~0G-+9~I@yoOH+~K_AUGNgk zst3<)cD-Biu0}9VF!a~Eb*{Th?*FZ?`~RNNRYR`j&>^RL_5W*URhT`TeBOTH?bml_ z)D>AR-0WQVWy;)#M;0Hyb8ODL#k-Baeh7(+`K44p=VvpYtks8_8=qCw)WTMRMhYrU z_{=uz&AYp+@W~0moSd92Ynj0Qt?zEndT}Nr%Kq}MW9NQGyhwIQce}jE{i=NAcbUtR zU!2L1`n&uhbBB+O#{NH_xa-#bo$>XdPLmqD;oJ9_EUqkxnfZ~~hIjp!2j8)Ox#ND) z5|c+8pUWp7n#b7_W!lADFMnY9RQ}>;XC6K~J9}#OW5(keEi2MaPt#5Q^yFmuoPEkI z0;REGVPec|d<#IM16#KxpIWF1nn(82*|j%#dGO@riD}1Y*4X{{%wF`4{qDcR4p#3( zKfitIedHgIo&~oR#6{LIXQgyxlq1*5BH-C89%D<*IuwL`*lrOU`beI3D zU|+jV#^DfL2Ot-0pfi|7l`k;x5pl%8RSR^ehj)Jfsc>x%j@g?zO&5&gMy0ew|jFaX8Gjh>4DDf zl9ZJE{^jrI=jT7W^s8$GPBln8&l1J9Wz91Vc^%_y<7KjQkG;6MSpL*E*S`-QJUk%% z`u>8;7yNkSjAn>xhuzt>?hLa)=_IO7AZKp)?K_{e>0{6-(hNP4#%-qA*TBcV|NVRN z+_|)Sdn&)Yzd!%(?s8!PfdE@`|0~{=Gb?lE@1Hw)|2&u4(B7?!O?jhC7Cdg~+rppQ z-?@`>>CvTs{(ipyXX7LOb#r&RC6+1r#rc5-2H!2-wb&piWaruXQ#y_+PTr!6Q7ylZ-`X${jdlevq&-%Vd; z2b#58dUUBz!OR8P`M1~W9^NnMw{h7F-1EnO3i>yTiiv&t^?LnhPYIxR3Ld<+Hac=c0%Nzh{@hM&>WC*jZQ%QctbjKjV0x?CFKh z?Vt@JmzViEFAWOKt_fb|^YGKt(>4G9eg_?&0=lCBG&*8qYb$A8rqj+Z&zA7~+}s~O ze}Yo{f*dQqkPwmCX1S}b&2afUdG4&UhwgD}YH1{0IOJ$jy+_iVJMYtR^ZE~mXUJ=u z&{}X@+SX$CXPx^!KiVE-9^CWks&@jK6*mb=SHRW!?XJcP4)^meECtbsCkj$R+H2p=7&GNdE)u(sgIf8 zZI53|zhv#M@3;Lg;i*1pN7*weef@e#Z|l=qN2TUUo?V_ezo_nf<-NbJV`HBm^T>25 zNi(^)cX8#Tt?DlXt}!-j&0qiY$Fave|EnzGV)tmA(Mo6Z*UrtWWheyTZmz2N1H)GbUupe``->;l~W{;0r&b)x-LVr({ zS^4K#p4^mpy2Rc+w)@bLH(|?v-qY%u_~8hQGEo>H|-O$K+4UDkw16 zUH;xIV{z{7Z6czgg}=VM%<9iS+{P;*DJdwk=Ku7j)r3Z__Vm2!w!USPY%Dq5xFYXd z@sF?H4xhQjBb_eYq|}rldc$jdp7H-Qo3Gb`BW^_|J#zHBaU4g4q9MyEg5pE!^>Q$3>$BZ+O=u=FL|> zo%L$cq)9?sqI9Kt+e%+wySTr;UPeYn!YoI^{{J82qeqYK+QrGqsT03%&V&ErJ-x+u zZ+foTbGh>A4birG<2m1x@24Nw!8?cy0gzBtmn%7Pl0v;7Zz1>FZ*NJ`rU7@ z`}OI&Hz^h|+wcGWId^^i*XY$B5;T z#_d+e=gqZ|{PuH$min)NYlZwj;u&8G`SEM=$~kcon;)ec zetW+0$^8@RRvMuBt_>TuDp(os^4hqzwpzE^{`Tq5#q-m{{^)?5!**8s!_)3~q5tkR zQy4c+}O|c~8?}R4_A}R{Q&#VbK$hy;YHs zkv{Y7=3ZMHot&S)es;P&Xodf$&)RaOJHpY1Is^WF>fW)#f>0cC``v>uR?iQ++jGc=qYCX!zoSQsobId;dyo&g^lV+x9=x!!wXm^Lfy!JJ0M* z>(*?%cm652*5;s1d0TfsW#Lk0t=s$e%d1r}Av=EUm{egS_%+ltU|Ishk1t;?=3l?M zElTL|74DTf6nvR2o~=E1cAbCO#oCba9^bsZ z#}BDrX!mZ{+o)9|%GoZgp{p5f{N-Kz`)jiU|8|LpiY~mGb@KG-0m;`)Ngd{gB$ek?!mvC-sne%s1cO20 zx?;Ib=jD$3GuN)x&7GHfc$MjDp1zYNa)HlzrA#_BgO_c{xTpl$0{`s%{PWrC_oi() zUbDaMFKCl9Xcg0m35ph$mJ0(kI{Nyat=)dlY4zGW=h)ob+(h5St`1xMwGOgeqVLX~ z7%ncZce_NFIV}X8KLr{HUbILF)Ti51`I&>4H}&72pFT4T9`@ZA?_ZYWYuI|Tb;gXD z32C`y$9J{fY<&~AXSVQc{{{P2EmK(S@czfyKeI#gKF>cB`m4X~wEWxY@1=t7=^c2Y zHaY*c->su2Yh>46e3Er4X@gPE!DBZ;^RQF<-rncB!m3kUC|TC~#$xwlo%V3~WseW^ z?Rp&Ac@?zckujg;!{ZzBKWlHk|1$5&>@D4yN8WX(o>-(+t5u>YzC`)xW3N zH23VPy7+c#-WJZh;@ictHWd?Cxy9~$HoY#=>sEZ->gj)eUw{AfmzS2-{P}qN#*G^Z z&r;l~oUHWD#>kb-m|C{oFFp0Y^`2<CaQu`G65@8{{s zX=rG;W7jUDygL?qlUZ4Rah|`sW9QC^bLOlGpEhaIp{d&8JkS2Lx3z)R6(%Jmy}0OH zukz(EzkSNbM@NqwKfX9bYvbn4j~^Ut{_*2SLQ;~``nbKIJrt)-b?HWf)~IMLF$qpu zXSCd4w}9`uXMHDsK6kcL|JPsPl-~a7#^?UTrx~qI3lrX7+PkK{{IBiS$g2^$Il32~ zWX>orm2*2~ZEs_?+01$JYRAdT4LyG3oB!Xwsb`AYpZvrB4Z>#%-?g3<`Z)aIr8}+1 z-b&vOUc(@J$ZA97v9xdXJLexLYNm}Ps;{e_lE%UKpL@sp?CKR)H{Mn0Ha&Xv(#t*9`AR|`?Fs(<^zx!j!MyWb{_LKd zmlHMZ%)jF7^QZVk{>)w(dNkX3!Kb;8cUMYSzuWq>%$nb8lfE3+Vz=H$f4|>XH!_-( zc)0E2ii!XF}o8+!op;1Dki`u5Rxt~as^EwgvV93zP`TR zxb#&>OiYZ1&XxKpmet>KzP-D9diDB!rxv+(gZ8_qs;aJu+}tKK%WAN&9P zdJRgGTXJqrTC+w6bP!Y6>ZR%D=Y6j$cG-JfZ3l0AoyC!vZ_{~}Ik-Q_qisdEx7KEx@v)})J@(=sRncsH&n=*AScloCOr~mrc zo5?CK72N0lvnc1V;kmaq?zhu#zk2mF^~pr3=}}LM|4*K|b!OhIi`n*jj{Q0C6O|s2nb_}%e zEnHmxUfvh8<=f}(%iVN;skeC8`ncNo>#@R@E?+Jz+xOXoEjTzBbiv5v$-KX?YJPJf66c1l4l}I!k^wsDM9M73 zGb$}r{9W}#(ema zAT!%yQ{Ahob2-+{YT?blR?RXM%s%0FQtd%6Lr+g{#U8`2s~t7qhl*sW|kZcg@$<&Q4CQ zRwiw2Z8koc6U%&O?U)lJc7T z7x=rz?_GRpQqbN;rfl`>*z_ff8ksWWq{?xa7=2Xe`e?Leq zyS7ZHx^U*bsxGsGxf;59s{i~CyKc4?fA?hXwfBKhHQ85oZ`$blZO)mmJ6<#F>igaC z-hDxU2G2j+E8Es&N6z{4Q>B@W$H+oW`S+uRhUM>Ko}HUpefzPWpWk-bqw{U6x7|B0 zC@8o+_xQDI)AaZKIJ96-4foml;(`Lu0gz|r+yCE|>vVISLul=(fQb(tJn&j?FYvYb zlZR@XeinYUI%|#0nv8E1#>f5|@l}7{WVvI#PIarUd`{y1=f7_l6?0E3 zo3`cE)h@n7*&Bj;n^n*29$Y+^UAWwLO~`D{cHs`bu4_jPPv#2kRr|K+{qEY*Xf~%v zrN`1wPOq4{cthE?v_pNH%C7I*D^|KmiaVP7PV{c$Pwf-8yeQPZS(^3gNc8P{Ntdf< z|9JIuy@saN!L?JRYwx{0Ja1`r$6aaf9nKc9ZDolcjyJ!rn%}q1de!#R9y^>Z&ewjB z@wGBd4BYR^pd#a^a7ed_kJ-xL&E~|ZeOu>DT`VfgUiPG4wEj+6NdB8=8;|JpKb$Ri zzd#wbpok~VV#YaV>-%4=tT$P6 z;y2t~!2I^&v1af3Sf{yb+-gI8k3Hqtv%4yM-Cxbrx8~J*ByV2d{Il}d`IPwcX%F0) z-)Bx={Hd{NM{VJoUn$~q<6rcwoT|xLuNq!zPMpBykt^QHM-d2#-jC&xG13z?bsEc>79mFcN@++6PY_MQ6;Q$9}M zvVJ$UGxuzZY0Ejzqf2LUx87|1v+vJ$tLuf=v$n43jp$yzS{t+$>R@4O*e14*`cJE` z-+%k{^rM5I@nKNE$9uZo)34X#*KaGE>ZK}cUH0U`!RD*ip6!KgvGF^-AHJ~c)wMiT zr~C8o|3CTubY1$RbzSp~!me)QJ^tg_)qJ^G_uD^Z+8jG}EVORrw5YYqf}i_MS>YwI zw`|M7noqYTl^OQ>zRy+4i;roY>(`*?y!*lR+y43UD;=&Ewrr~Xu>I`)V=LqRUY0JH zKkw6)dwhB;#e*U(>r_4K)e|%GPX<5qe`tT`euDh(-COgM5}VT9{h5xlUfNTA)9&~G zcT1Q>%Puj-s@v)-_v^~%JUsKw_{oxIFW9(3^t8%c-ZLCnJeU3a`PzF6avNUw9Ntv4 z`cU}C<6ZU=yDN3>-F}$uLKljeR`TeC`+1op<`}glZSm^n!mC^HCc6jBc z_;aUDb=|phr#dV9)02}mKOVO4nr~hHPUmEutlG(chZ7G5bHDgl#edg(%e(7`r@eXp z?AfQk-|q{HiM1_GzWV(B@ngq?E*LWyiyODq+sj0U-+sj(bVc&&UCVWTrIy=HX=rLC z{=M>5=EpCF`!#=go!j}=ZkTuGrud>hNvjf#sI_6=cV2gqPdGQnvMTp#t4n-G``Lr} z{F`!a7@1T`CGG24sIdCM#CH=Sqoa+hzPzaR`ob8sCF9_~y}u204W2Gmx4)EItNCjA zjP*&c?l@g5Dz)6U`{a%}2PggWw%GKEBRg#U&fG=(M(Ob!ibtFu+`r7Pb?NzByDyc? z&of*8KdGxe^HK@dI^EdHpQop5Z++ap!!|!ZpHJTE%!}*ppKmst&$zx{R#Cp?#Wpt) z{`JlG<(_=mcA4jIiAH<4{JP+|k9WyLr;28G-Te8{ugvZJk=v(dJG1kDod5sN^S=8N zK`Yu^=9VwFJjfq4Ju&u$(HifybNBDtv~lCZ_V@JrZdIdv<1BouvkL?e|e{T!__+iCqko#`Gogy-|pyqJlH=SF~k50PG@P?vFOJb<0pVqt$aN+g(q20LG`}?W)&a7TuUN`pDTHmkzF8li0`o%kzNbFazx8ixz z;J={%%v*7O+uW7Yw2!YVNO>#R_rGM$oE{mu`bULvl`lmP9XhmO>)!usGgWVjv!%?h z{kY(y-EYy=+WHxSTb=9V->q4&pyBLn^Uq(q*8j|XoiCx?`|;Hy*VPNcws7VxuAaO_ zp!Dw9rrAf{_CEfSzua>BUzwTV#+mmvS%PN3KxcBijomrDKXjVT1yQFCE2l}eih1V` z9cp4^W&<6u6BHB#I^2Ht_MEiaYjW4j-zCRy&mYCMbxEp&_pFkfrj@Okx$QbE0cKV8 zU(bX-`gXi9@3*C%%npZ4PtCbj@ry=nIx_g0qQ+zmPlL|uKkRHys0j~vWKz3w&{M!oJF9~Y>o zsckBM9|xMYz4y1e(^kBD;zq&VX!B!>C$r1t+J4A*?$|8GE4Rw_n(N(C<(F)o$Avxr zSWjG%>>2axvrubgtHmyhOOHF{zPz~C{O<1V%gcPFdwR0|?YEQ!ElX8&+57O}!-J58 z4;l>D*T<*d-&gzQ{{H#R?R=juZ?|8lc_kq&v+Z~Noy;ri1mES}{h0B(5j02a+mrp1 z%O!T&%o(1aI#Y!H`>Fh_F!HQdpEz||>%@tIacB3wxU(AayB@s?{x)%Ly_{~O zW@LNV;Cd6|8`OnVWG@kS0D@(Q5`-Mw(up}=v)bouk z$a>b&yqvkuSEexQRm6QcI<_P@&Bb-yo&uc@ugyu6H8YwD_L>lb`_vfmH1?9JwqkB?8r?QOcb zx3^u@-e37y?fJR6kKf$fJjbqf7Pq)w!NV7YTed8%wtyy?0kGlvy;aFKlaJWdD}gtmZo>mNdVal5-{eYD(;Br`k~8XG?vI^wTHR zsm^^g`zS+1MC5~;S0<<3JIhgF)DZSRyLJA2`L$7751l)=u1tT4l*cB0@G=s{;Naj} zTQY+mZtJkLv^;YBICSaD>3@4lCVC&&n>c5RPhQ!(wPj~~HAFM5Zn8;> zORte#yRmG`u~QehwArQ0e01$ZX}NcDC0VyP*T0LasIGqe^l9t#>EgUnCM%AvuKEC3 zQPQ$PNm_a~*L=1qr%#`Ld}^w8%-*W0PoAU*2@CHmdn*+#w7%f$hfuwk{SIkWYCkJ~ zR^1PtrM0ne%fVX*rM%s}&*Yvp+-!LG&apq&+*A8n*RqOfamUW=+{w8tY?;JtiR84* zwvCy0BQw@q&C)!f#h?pXW)v0Vl~|?-UUMWKFe5hN+sU0@HBV?cOkTXuVD*GKS7eHe zi%NsDPtBY$>%oI34U-oO^Gcf?*}hR+6@MUgw1rBu*j5 z+S=MtTeDnOhpl}Vv3|jVhA&@AE?vI7v+l3e%a<=xQc_-=x{_rY`AX+X_?48D)CqIv z#(oP-zhC-0so1-C$}+DrYt9&$86LZGN@S^Lt$9sE zdWyKHNZ=w(#yr)p|NnfRul#0va%ysG?Rtn-cO!9 z`QCEJW;Xt-dw$+h0WCY{P;}AVxM9PA!-tvq`TMo%&DGW2r~i5W{Q2bh^ZVTvE9&d_ z&omtD*_asIryMtGFg#*G^dHOx&-kDhvUZ5E5Q zwY3Nr>$h*;G(@=Wo-Zpa11)6B&h8FcdF0wPvFPaNyM-SgKojIgflV7X9z1re&8=7J zYMI6&zRBUoL5qolBsWP9aWCPE5y+ z9n%ou>XWmrI{y^3QbNGgb5gD1Du;yu2}wys_n~5rWse0H3E9}%YHDikd=FY@=+rSw zMH#%dLcr-tFf`iW7Gqils`qgz{QZWTdE>^7M~)tS`0!!i?S&mxTK4w)w>b+qaVW}h z8bPuj$oa~@4>@#;>1JG9#F~9=&C0g{F1A~%zGkuUN`>4`1ljO)UJrOt5h4gMT(aoW znzd^KHAJfBSAKqWwJhS1-I|!4LEqnBx^rjGw!?x>9EvWclT+eDqVn?ej`ztjFR1_j zFJo&I=-L6xRj;Pbp6&hl*;!6LzIAT{vW`4B*nBZ-D+?RjwK5G*=v6R)HVnvQXJ`NU z`&Zj$+0`sfUEQTywrpuzECiBYQMc*qGJ*e~HK<3s#j~w>Kt*0hmLhUUcK_a%c$jUk z>uyd?P97-}4r^;`8Ch9QK0Y%E-Rl`9pz}85<>hsBbqfm$7R2wb%el3sbJ8TCy}L3q zGeIY+`1||&`1%%>m8qGSOnH2~|Grt<=jZ2*3m!Pg$jX+cPFDAKTNp55`gC_6pPqFw zD?`F=xFseojFXX+l=PWlz?h$(@8jzW+S>7Q`TVr+@9ust)qnRS?p)->t;?4$XD)aL z-`aiZRG0d^il+PZ|Nm}#zJ6VGd|e&LJ68>DiAx zYc_4VbTK?KQu6KV7cVvxSmdl-yLQ^Vd3mj^t%k|x=U67&%|9O%74_!#_xENtHaX(@ zaVDPE?%k7zz9>*?D=}0yI+c@9*3C=uwilxA*40zP`w04L!YW-@cXo z{`PiuX=&-rM@PHgetv#FE$!LYs@&`AVhgX^7x#bs_%Y-5g$omY{;fNG_UzVU$K0f) zr8g%SWHdS)2nq^Xvu<79xt$&9>xz#>SI5WCPfbt1y()C|nx#vRIxU<~T3#NVnYnUp z^!9T$a{g!jK2!c~EN4@ZQ1gd@!QlUoT<-S$y5FRwrDeZQ7770F^KaeloyE%^%dxVu z8Xo=EDXi`@3AAA>?aqY@6Dn=wZtp66y~fa{@{`KDU(bTx%(tn$q;m7`@9)XS+>``Q zpFX|$%a;;qIk~bskIRb$&&NFP+5T3iR)r^2F*6lzqUGjy@`RrgO0@8+j2Xj`czi#+ZQ(}Brs6$^1F`}AwfZlK7K5; zt^aqY@b3F3N*9ZdMgQKMeqO~hH8s`nZDnPpmWD<_&;RXD-{Il}V3`j$XaH{Qau^`{PwrRZpEd6;xSSx#hsrW#7N{_x5IHWqH}nKmV~cZhidz zX)0g8ezi>cP_>sc`n-=!W@hH8GiOS^Jb(7gYf@W#yKL?L$=xpJEpOjaS5p(=YMnA| z+OpKs(=305uZb{x{paL4TLy+7)*`<>K<7Bh+SkSC$L+areSQ4Z3l|iOgB1iWoSkj1 z>T7@S+_`htCaHRV`TX4f=9g{8$FtARv#q!JKL107&8MF=T`o#otxkRa`sMAtX4O5K ztI;{{^5c&h#>T-DJzO3Z+_-3OW%WwJUVbM#0|R$=lDy}{xpUXfn7K8)G*&HxV8-*~ib7zcVl- zBnG}w&OA5Ye*L?5dA>eAFD@_l-&OW@))^nr$$5P%SFXI+#w%U&>WZdq?XQsJgP zWn~3vhy>;5ukVwwT(oeZ;@w@P+Pk)@1q#_dpQ;@`W%A_3uU=(s-L_3jhVS*=-Q`wg zZ*HuKu6_n~V592e{Y@X=i@i6ITYo)!Qc~RY_3_)!o%3`5z0`a9nKLqPMMXu^(!M=A zD{Wo=Zcgs4EhmkPjC9o1k8jJrpI2I1YG!IGYW#k>e*Bw5t=!w{{#JSW`fh!CdO9cv zZr!%c?f4ln=RNZ8=gpJT*3vRse?9x>&!1t@(b;KfU%!6sd)+T*E44Q-CFRJf)vM3G z{~n!{wd!Y$owm02=55=y#UJ~4zv!cO@m%A3x0(+wkhLf{uxZn#MaLV=Oid#_Jw3nd zy1d-~eDu5Y0x5n525#k}XL9!an{eR&vQ?|JJTEPF@1G>YdFR!`!|hfzKQ{dM!N`zM zx3QO + 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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 literal 0 HcmV?d00001 diff --git a/Objects/typeobject.c b/Objects/typeobject.c index 2ef79fbf17..6df9986b82 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 bfb378c4a4..ce208aac9c 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 ce0dc235c5..82f2171f1e 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 a37c1b144e..a48738835f 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 4524382e4f..7e4137a8e3 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 e8e2397b11..6ee794a05b 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 df73cc091d..b4a1da8aec 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 c1edf8842f..f1e32d05af 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 ddafcf99ca..4261378d45 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 656667ac93..74165acd83 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 8dc590b4b8..f7ed98ff60 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.