diff --git a/Include/internal/pycore_code.h b/Include/internal/pycore_code.h index 5b1fddbe15b98b8..5e06d3d48c8b796 100644 --- a/Include/internal/pycore_code.h +++ b/Include/internal/pycore_code.h @@ -111,6 +111,7 @@ typedef struct { union { uint16_t keys_version[2]; uint16_t dict_offset; + uint16_t validity_offset; }; uint16_t descr[4]; } _PyLoadMethodCache; @@ -121,6 +122,13 @@ typedef struct { #define INLINE_CACHE_ENTRIES_STORE_ATTR CACHE_ENTRIES(_PyAttrCache) +typedef struct { + _Py_BackoffCounter counter; + uint16_t version[2]; + uint16_t validity_offset; + uint16_t index; +} _PyLoadAttrInstanceValueCache; + typedef struct { _Py_BackoffCounter counter; uint16_t func_version[2]; diff --git a/Include/internal/pycore_opcode_metadata.h b/Include/internal/pycore_opcode_metadata.h index 31d2e31573d8c53..3f4fc832920c4b6 100644 --- a/Include/internal/pycore_opcode_metadata.h +++ b/Include/internal/pycore_opcode_metadata.h @@ -1455,13 +1455,13 @@ _PyOpcode_macro_expansion[256] = { [LOAD_ATTR_CLASS] = { .nuops = 4, .uops = { { _RECORD_TOS, OPARG_SIMPLE, 1 }, { _CHECK_ATTR_CLASS, 2, 1 }, { _LOAD_ATTR_CLASS, 4, 5 }, { _PUSH_NULL_CONDITIONAL, OPARG_SIMPLE, 9 } } }, [LOAD_ATTR_CLASS_WITH_METACLASS_CHECK] = { .nuops = 5, .uops = { { _RECORD_TOS, OPARG_SIMPLE, 1 }, { _GUARD_TYPE_VERSION, 2, 1 }, { _CHECK_ATTR_CLASS, 2, 3 }, { _LOAD_ATTR_CLASS, 4, 5 }, { _PUSH_NULL_CONDITIONAL, OPARG_SIMPLE, 9 } } }, [LOAD_ATTR_GETATTRIBUTE_OVERRIDDEN] = { .nuops = 7, .uops = { { _RECORD_TOS_TYPE, OPARG_SIMPLE, 1 }, { _GUARD_TYPE_VERSION, 2, 1 }, { _CHECK_PEP_523, OPARG_SIMPLE, 3 }, { _LOAD_ATTR_GETATTRIBUTE_OVERRIDDEN_FRAME, 2, 3 }, { _LOAD_ATTR_GETATTRIBUTE_OVERRIDDEN_FRAME, OPERAND1_4, 5 }, { _SAVE_RETURN_OFFSET, OPARG_SAVE_RETURN_OFFSET, 9 }, { _PUSH_FRAME, OPARG_SIMPLE, 9 } } }, - [LOAD_ATTR_INSTANCE_VALUE] = { .nuops = 6, .uops = { { _RECORD_TOS_TYPE, OPARG_SIMPLE, 1 }, { _GUARD_TYPE_VERSION, 2, 1 }, { _CHECK_MANAGED_OBJECT_HAS_VALUES, OPARG_SIMPLE, 3 }, { _LOAD_ATTR_INSTANCE_VALUE, 1, 3 }, { _POP_TOP, OPARG_SIMPLE, 4 }, { _PUSH_NULL_CONDITIONAL, OPARG_SIMPLE, 9 } } }, + [LOAD_ATTR_INSTANCE_VALUE] = { .nuops = 6, .uops = { { _RECORD_TOS_TYPE, OPARG_SIMPLE, 1 }, { _GUARD_TYPE_VERSION, 2, 1 }, { _CHECK_MANAGED_OBJECT_HAS_VALUES, 1, 3 }, { _LOAD_ATTR_INSTANCE_VALUE, 1, 4 }, { _POP_TOP, OPARG_SIMPLE, 5 }, { _PUSH_NULL_CONDITIONAL, OPARG_SIMPLE, 9 } } }, [LOAD_ATTR_METHOD_LAZY_DICT] = { .nuops = 4, .uops = { { _RECORD_TOS_TYPE, OPARG_SIMPLE, 1 }, { _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 = 3, .uops = { { _RECORD_TOS_TYPE, OPARG_SIMPLE, 1 }, { _GUARD_TYPE_VERSION, 2, 1 }, { _LOAD_ATTR_METHOD_NO_DICT, 4, 5 } } }, - [LOAD_ATTR_METHOD_WITH_VALUES] = { .nuops = 4, .uops = { { _RECORD_TOS_TYPE, OPARG_SIMPLE, 1 }, { _GUARD_TYPE_VERSION, 2, 1 }, { _CHECK_MANAGED_OBJECT_HAS_VALUES, OPARG_SIMPLE, 3 }, { _LOAD_ATTR_METHOD_WITH_VALUES, 4, 5 } } }, + [LOAD_ATTR_METHOD_WITH_VALUES] = { .nuops = 4, .uops = { { _RECORD_TOS_TYPE, OPARG_SIMPLE, 1 }, { _GUARD_TYPE_VERSION, 2, 1 }, { _CHECK_MANAGED_OBJECT_HAS_VALUES, 1, 3 }, { _LOAD_ATTR_METHOD_WITH_VALUES, 4, 5 } } }, [LOAD_ATTR_MODULE] = { .nuops = 4, .uops = { { _LOAD_ATTR_MODULE, 2, 1 }, { _LOAD_ATTR_MODULE, OPERAND1_1, 3 }, { _POP_TOP, OPARG_SIMPLE, 4 }, { _PUSH_NULL_CONDITIONAL, OPARG_SIMPLE, 9 } } }, [LOAD_ATTR_NONDESCRIPTOR_NO_DICT] = { .nuops = 3, .uops = { { _RECORD_TOS_TYPE, OPARG_SIMPLE, 1 }, { _GUARD_TYPE_VERSION, 2, 1 }, { _LOAD_ATTR_NONDESCRIPTOR_NO_DICT, 4, 5 } } }, - [LOAD_ATTR_NONDESCRIPTOR_WITH_VALUES] = { .nuops = 4, .uops = { { _RECORD_TOS_TYPE, OPARG_SIMPLE, 1 }, { _GUARD_TYPE_VERSION, 2, 1 }, { _CHECK_MANAGED_OBJECT_HAS_VALUES, OPARG_SIMPLE, 3 }, { _LOAD_ATTR_NONDESCRIPTOR_WITH_VALUES, 4, 5 } } }, + [LOAD_ATTR_NONDESCRIPTOR_WITH_VALUES] = { .nuops = 4, .uops = { { _RECORD_TOS_TYPE, OPARG_SIMPLE, 1 }, { _GUARD_TYPE_VERSION, 2, 1 }, { _CHECK_MANAGED_OBJECT_HAS_VALUES, 1, 3 }, { _LOAD_ATTR_NONDESCRIPTOR_WITH_VALUES, 4, 5 } } }, [LOAD_ATTR_PROPERTY] = { .nuops = 7, .uops = { { _RECORD_TOS_TYPE, OPARG_SIMPLE, 1 }, { _GUARD_TYPE_VERSION, 2, 1 }, { _CHECK_PEP_523, OPARG_SIMPLE, 3 }, { _LOAD_ATTR_PROPERTY_FRAME, 2, 3 }, { _LOAD_ATTR_PROPERTY_FRAME, OPERAND1_4, 5 }, { _SAVE_RETURN_OFFSET, OPARG_SAVE_RETURN_OFFSET, 9 }, { _PUSH_FRAME, OPARG_SIMPLE, 9 } } }, [LOAD_ATTR_SLOT] = { .nuops = 5, .uops = { { _RECORD_TOS_TYPE, OPARG_SIMPLE, 1 }, { _GUARD_TYPE_VERSION, 2, 1 }, { _LOAD_ATTR_SLOT, 1, 3 }, { _POP_TOP, OPARG_SIMPLE, 4 }, { _PUSH_NULL_CONDITIONAL, OPARG_SIMPLE, 9 } } }, [LOAD_ATTR_WITH_HINT] = { .nuops = 5, .uops = { { _RECORD_TOS_TYPE, OPARG_SIMPLE, 1 }, { _GUARD_TYPE_VERSION, 2, 1 }, { _LOAD_ATTR_WITH_HINT, 1, 3 }, { _POP_TOP, OPARG_SIMPLE, 4 }, { _PUSH_NULL_CONDITIONAL, OPARG_SIMPLE, 9 } } }, diff --git a/Modules/_testinternalcapi/test_cases.c.h b/Modules/_testinternalcapi/test_cases.c.h index a17648a33d4fe4a..b78b0a3d1d89e41 100644 --- a/Modules/_testinternalcapi/test_cases.c.h +++ b/Modules/_testinternalcapi/test_cases.c.h @@ -8969,10 +8969,13 @@ } // _CHECK_MANAGED_OBJECT_HAS_VALUES { + uint16_t validity_offset = read_u16(&this_instr[4].cache); PyObject *owner_o = PyStackRef_AsPyObjectBorrow(owner); assert(Py_TYPE(owner_o)->tp_dictoffset < 0); assert(Py_TYPE(owner_o)->tp_flags & Py_TPFLAGS_INLINE_VALUES); - if (!FT_ATOMIC_LOAD_UINT8(_PyObject_InlineValues(owner_o)->valid)) { + assert((char *)&_PyObject_InlineValues(owner_o)->valid == + (char *)owner_o + validity_offset); + if (!FT_ATOMIC_LOAD_UINT8(*((uint8_t *)owner_o + validity_offset))) { UPDATE_MISS_STATS(LOAD_ATTR); assert(_PyOpcode_Deopt[opcode] == (LOAD_ATTR)); JUMP_TO_PREDICTED(LOAD_ATTR); @@ -8980,7 +8983,7 @@ } // _LOAD_ATTR_INSTANCE_VALUE { - uint16_t offset = read_u16(&this_instr[4].cache); + uint16_t offset = read_u16(&this_instr[5].cache); PyObject *owner_o = PyStackRef_AsPyObjectBorrow(owner); PyObject **value_ptr = (PyObject**)(((char *)owner_o) + offset); PyObject *attr_o = FT_ATOMIC_LOAD_PTR_ACQUIRE(*value_ptr); @@ -9013,7 +9016,7 @@ PyStackRef_XCLOSE(value); _PyFrame_StackPointerInvalidate(frame); } - /* Skip 5 cache entries */ + /* Skip 4 cache entries */ // _PUSH_NULL_CONDITIONAL { null = &stack_pointer[0]; @@ -9157,16 +9160,19 @@ } // _CHECK_MANAGED_OBJECT_HAS_VALUES { + uint16_t validity_offset = read_u16(&this_instr[4].cache); PyObject *owner_o = PyStackRef_AsPyObjectBorrow(owner); assert(Py_TYPE(owner_o)->tp_dictoffset < 0); assert(Py_TYPE(owner_o)->tp_flags & Py_TPFLAGS_INLINE_VALUES); - if (!FT_ATOMIC_LOAD_UINT8(_PyObject_InlineValues(owner_o)->valid)) { + assert((char *)&_PyObject_InlineValues(owner_o)->valid == + (char *)owner_o + validity_offset); + if (!FT_ATOMIC_LOAD_UINT8(*((uint8_t *)owner_o + validity_offset))) { UPDATE_MISS_STATS(LOAD_ATTR); assert(_PyOpcode_Deopt[opcode] == (LOAD_ATTR)); JUMP_TO_PREDICTED(LOAD_ATTR); } } - /* Skip 2 cache entries */ + /* Skip 1 cache entry */ // _LOAD_ATTR_METHOD_WITH_VALUES { PyObject *descr = read_obj(&this_instr[6].cache); @@ -9342,16 +9348,19 @@ } // _CHECK_MANAGED_OBJECT_HAS_VALUES { + uint16_t validity_offset = read_u16(&this_instr[4].cache); PyObject *owner_o = PyStackRef_AsPyObjectBorrow(owner); assert(Py_TYPE(owner_o)->tp_dictoffset < 0); assert(Py_TYPE(owner_o)->tp_flags & Py_TPFLAGS_INLINE_VALUES); - if (!FT_ATOMIC_LOAD_UINT8(_PyObject_InlineValues(owner_o)->valid)) { + assert((char *)&_PyObject_InlineValues(owner_o)->valid == + (char *)owner_o + validity_offset); + if (!FT_ATOMIC_LOAD_UINT8(*((uint8_t *)owner_o + validity_offset))) { UPDATE_MISS_STATS(LOAD_ATTR); assert(_PyOpcode_Deopt[opcode] == (LOAD_ATTR)); JUMP_TO_PREDICTED(LOAD_ATTR); } } - /* Skip 2 cache entries */ + /* Skip 1 cache entry */ // _LOAD_ATTR_NONDESCRIPTOR_WITH_VALUES { PyObject *descr = read_obj(&this_instr[6].cache); diff --git a/Python/bytecodes.c b/Python/bytecodes.c index 4d7b338e2dbd4c3..0fb27ede6c7d50d 100644 --- a/Python/bytecodes.c +++ b/Python/bytecodes.c @@ -2886,11 +2886,13 @@ dummy_func( EXIT_IF(tp != (PyTypeObject *)type); } - op(_CHECK_MANAGED_OBJECT_HAS_VALUES, (owner -- owner)) { + op(_CHECK_MANAGED_OBJECT_HAS_VALUES, (validity_offset/1, owner -- owner)) { PyObject *owner_o = PyStackRef_AsPyObjectBorrow(owner); assert(Py_TYPE(owner_o)->tp_dictoffset < 0); assert(Py_TYPE(owner_o)->tp_flags & Py_TPFLAGS_INLINE_VALUES); - EXIT_IF(!FT_ATOMIC_LOAD_UINT8(_PyObject_InlineValues(owner_o)->valid)); + assert((char *)&_PyObject_InlineValues(owner_o)->valid == + (char *)owner_o + validity_offset); + EXIT_IF(!FT_ATOMIC_LOAD_UINT8(*((uint8_t *)owner_o + validity_offset))); } op(_LOAD_ATTR_INSTANCE_VALUE, (offset/1, owner -- attr, o)) { @@ -2918,7 +2920,7 @@ dummy_func( _CHECK_MANAGED_OBJECT_HAS_VALUES + _LOAD_ATTR_INSTANCE_VALUE + POP_TOP + - unused/5 + + unused/4 + _PUSH_NULL_CONDITIONAL; op(_LOAD_ATTR_MODULE, (dict_version/2, index/1, owner -- attr, o)) { @@ -4247,7 +4249,7 @@ dummy_func( _RECORD_TOS_TYPE + _GUARD_TYPE_VERSION + _CHECK_MANAGED_OBJECT_HAS_VALUES + - unused/2 + + unused/1 + _LOAD_ATTR_METHOD_WITH_VALUES; op(_LOAD_ATTR_METHOD_NO_DICT, (descr/4, owner -- attr, self)) { @@ -4281,7 +4283,7 @@ dummy_func( _RECORD_TOS_TYPE + _GUARD_TYPE_VERSION + _CHECK_MANAGED_OBJECT_HAS_VALUES + - unused/2 + + unused/1 + _LOAD_ATTR_NONDESCRIPTOR_WITH_VALUES; op(_LOAD_ATTR_NONDESCRIPTOR_NO_DICT, (descr/4, owner -- attr)) { diff --git a/Python/executor_cases.c.h b/Python/executor_cases.c.h index e45bbd7cceb295f..7bfab63f5780dd0 100644 --- a/Python/executor_cases.c.h +++ b/Python/executor_cases.c.h @@ -12017,10 +12017,13 @@ ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); _PyStackRef owner; owner = stack_pointer[-1]; + uint16_t validity_offset = (uint16_t)CURRENT_OPERAND0_16(); PyObject *owner_o = PyStackRef_AsPyObjectBorrow(owner); assert(Py_TYPE(owner_o)->tp_dictoffset < 0); assert(Py_TYPE(owner_o)->tp_flags & Py_TPFLAGS_INLINE_VALUES); - if (!FT_ATOMIC_LOAD_UINT8(_PyObject_InlineValues(owner_o)->valid)) { + assert((char *)&_PyObject_InlineValues(owner_o)->valid == + (char *)owner_o + validity_offset); + if (!FT_ATOMIC_LOAD_UINT8(*((uint8_t *)owner_o + validity_offset))) { UOP_STAT_INC(uopcode, miss); SET_CURRENT_CACHED_VALUES(0); JUMP_TO_JUMP_TARGET(); @@ -12039,10 +12042,13 @@ _PyStackRef owner; _PyStackRef _stack_item_0 = _tos_cache0; owner = _stack_item_0; + uint16_t validity_offset = (uint16_t)CURRENT_OPERAND0_16(); PyObject *owner_o = PyStackRef_AsPyObjectBorrow(owner); assert(Py_TYPE(owner_o)->tp_dictoffset < 0); assert(Py_TYPE(owner_o)->tp_flags & Py_TPFLAGS_INLINE_VALUES); - if (!FT_ATOMIC_LOAD_UINT8(_PyObject_InlineValues(owner_o)->valid)) { + assert((char *)&_PyObject_InlineValues(owner_o)->valid == + (char *)owner_o + validity_offset); + if (!FT_ATOMIC_LOAD_UINT8(*((uint8_t *)owner_o + validity_offset))) { UOP_STAT_INC(uopcode, miss); _tos_cache0 = owner; SET_CURRENT_CACHED_VALUES(1); @@ -12061,10 +12067,13 @@ _PyStackRef _stack_item_0 = _tos_cache0; _PyStackRef _stack_item_1 = _tos_cache1; owner = _stack_item_1; + uint16_t validity_offset = (uint16_t)CURRENT_OPERAND0_16(); PyObject *owner_o = PyStackRef_AsPyObjectBorrow(owner); assert(Py_TYPE(owner_o)->tp_dictoffset < 0); assert(Py_TYPE(owner_o)->tp_flags & Py_TPFLAGS_INLINE_VALUES); - if (!FT_ATOMIC_LOAD_UINT8(_PyObject_InlineValues(owner_o)->valid)) { + assert((char *)&_PyObject_InlineValues(owner_o)->valid == + (char *)owner_o + validity_offset); + if (!FT_ATOMIC_LOAD_UINT8(*((uint8_t *)owner_o + validity_offset))) { UOP_STAT_INC(uopcode, miss); _tos_cache1 = owner; _tos_cache0 = _stack_item_0; @@ -12086,10 +12095,13 @@ _PyStackRef _stack_item_1 = _tos_cache1; _PyStackRef _stack_item_2 = _tos_cache2; owner = _stack_item_2; + uint16_t validity_offset = (uint16_t)CURRENT_OPERAND0_16(); PyObject *owner_o = PyStackRef_AsPyObjectBorrow(owner); assert(Py_TYPE(owner_o)->tp_dictoffset < 0); assert(Py_TYPE(owner_o)->tp_flags & Py_TPFLAGS_INLINE_VALUES); - if (!FT_ATOMIC_LOAD_UINT8(_PyObject_InlineValues(owner_o)->valid)) { + assert((char *)&_PyObject_InlineValues(owner_o)->valid == + (char *)owner_o + validity_offset); + if (!FT_ATOMIC_LOAD_UINT8(*((uint8_t *)owner_o + validity_offset))) { UOP_STAT_INC(uopcode, miss); _tos_cache2 = owner; _tos_cache1 = _stack_item_1; diff --git a/Python/generated_cases.c.h b/Python/generated_cases.c.h index 6178dc70c1b80e7..ac543b18f7eefdb 100644 --- a/Python/generated_cases.c.h +++ b/Python/generated_cases.c.h @@ -8968,10 +8968,13 @@ } // _CHECK_MANAGED_OBJECT_HAS_VALUES { + uint16_t validity_offset = read_u16(&this_instr[4].cache); PyObject *owner_o = PyStackRef_AsPyObjectBorrow(owner); assert(Py_TYPE(owner_o)->tp_dictoffset < 0); assert(Py_TYPE(owner_o)->tp_flags & Py_TPFLAGS_INLINE_VALUES); - if (!FT_ATOMIC_LOAD_UINT8(_PyObject_InlineValues(owner_o)->valid)) { + assert((char *)&_PyObject_InlineValues(owner_o)->valid == + (char *)owner_o + validity_offset); + if (!FT_ATOMIC_LOAD_UINT8(*((uint8_t *)owner_o + validity_offset))) { UPDATE_MISS_STATS(LOAD_ATTR); assert(_PyOpcode_Deopt[opcode] == (LOAD_ATTR)); JUMP_TO_PREDICTED(LOAD_ATTR); @@ -8979,7 +8982,7 @@ } // _LOAD_ATTR_INSTANCE_VALUE { - uint16_t offset = read_u16(&this_instr[4].cache); + uint16_t offset = read_u16(&this_instr[5].cache); PyObject *owner_o = PyStackRef_AsPyObjectBorrow(owner); PyObject **value_ptr = (PyObject**)(((char *)owner_o) + offset); PyObject *attr_o = FT_ATOMIC_LOAD_PTR_ACQUIRE(*value_ptr); @@ -9012,7 +9015,7 @@ PyStackRef_XCLOSE(value); _PyFrame_StackPointerInvalidate(frame); } - /* Skip 5 cache entries */ + /* Skip 4 cache entries */ // _PUSH_NULL_CONDITIONAL { null = &stack_pointer[0]; @@ -9156,16 +9159,19 @@ } // _CHECK_MANAGED_OBJECT_HAS_VALUES { + uint16_t validity_offset = read_u16(&this_instr[4].cache); PyObject *owner_o = PyStackRef_AsPyObjectBorrow(owner); assert(Py_TYPE(owner_o)->tp_dictoffset < 0); assert(Py_TYPE(owner_o)->tp_flags & Py_TPFLAGS_INLINE_VALUES); - if (!FT_ATOMIC_LOAD_UINT8(_PyObject_InlineValues(owner_o)->valid)) { + assert((char *)&_PyObject_InlineValues(owner_o)->valid == + (char *)owner_o + validity_offset); + if (!FT_ATOMIC_LOAD_UINT8(*((uint8_t *)owner_o + validity_offset))) { UPDATE_MISS_STATS(LOAD_ATTR); assert(_PyOpcode_Deopt[opcode] == (LOAD_ATTR)); JUMP_TO_PREDICTED(LOAD_ATTR); } } - /* Skip 2 cache entries */ + /* Skip 1 cache entry */ // _LOAD_ATTR_METHOD_WITH_VALUES { PyObject *descr = read_obj(&this_instr[6].cache); @@ -9341,16 +9347,19 @@ } // _CHECK_MANAGED_OBJECT_HAS_VALUES { + uint16_t validity_offset = read_u16(&this_instr[4].cache); PyObject *owner_o = PyStackRef_AsPyObjectBorrow(owner); assert(Py_TYPE(owner_o)->tp_dictoffset < 0); assert(Py_TYPE(owner_o)->tp_flags & Py_TPFLAGS_INLINE_VALUES); - if (!FT_ATOMIC_LOAD_UINT8(_PyObject_InlineValues(owner_o)->valid)) { + assert((char *)&_PyObject_InlineValues(owner_o)->valid == + (char *)owner_o + validity_offset); + if (!FT_ATOMIC_LOAD_UINT8(*((uint8_t *)owner_o + validity_offset))) { UPDATE_MISS_STATS(LOAD_ATTR); assert(_PyOpcode_Deopt[opcode] == (LOAD_ATTR)); JUMP_TO_PREDICTED(LOAD_ATTR); } } - /* Skip 2 cache entries */ + /* Skip 1 cache entry */ // _LOAD_ATTR_NONDESCRIPTOR_WITH_VALUES { PyObject *descr = read_obj(&this_instr[6].cache); diff --git a/Python/specialize.c b/Python/specialize.c index 773b55c329c7720..740df36342173e4 100644 --- a/Python/specialize.c +++ b/Python/specialize.c @@ -646,15 +646,30 @@ specialize_dict_access_inline( return 0; } assert(index >= 0); - assert(_PyObject_InlineValues(owner)->valid); - char *value_addr = (char *)&_PyObject_InlineValues(owner)->values[index]; + PyDictValues *values = _PyObject_InlineValues(owner); + assert(values->valid); + char *value_addr = (char *)&values->values[index]; Py_ssize_t offset = value_addr - (char *)owner; if (offset != (uint16_t)offset) { SPECIALIZATION_FAIL(base_op, SPEC_FAIL_OUT_OF_RANGE); return 0; } - cache->index = (uint16_t)offset; - write_u32(cache->version, tp_version); + if (base_op == LOAD_ATTR) { + Py_ssize_t validity_offset = (char *)&values->valid - (char *)owner; + if (validity_offset != (uint16_t)validity_offset) { + SPECIALIZATION_FAIL(base_op, SPEC_FAIL_OUT_OF_RANGE); + return 0; + } + _PyLoadAttrInstanceValueCache *iv_cache = + (_PyLoadAttrInstanceValueCache *)cache; + iv_cache->validity_offset = (uint16_t)validity_offset; + iv_cache->index = (uint16_t)offset; + write_u32(iv_cache->version, tp_version); + } + else { + cache->index = (uint16_t)offset; + write_u32(cache->version, tp_version); + } specialize(instr, values_op); return 1; } @@ -1289,6 +1304,13 @@ specialize_attr_loadclassattr(PyObject *owner, _Py_CODEUNIT *instr, SPECIALIZATION_FAIL(LOAD_ATTR, SPEC_FAIL_OUT_OF_VERSIONS); return 0; } + PyDictValues *values = _PyObject_InlineValues(owner); + Py_ssize_t validity_offset = (char *)&values->valid - (char *)owner; + if (validity_offset != (uint16_t)validity_offset) { + SPECIALIZATION_FAIL(LOAD_ATTR, SPEC_FAIL_OUT_OF_RANGE); + return 0; + } + cache->validity_offset = (uint16_t)validity_offset; specialize(instr, is_method ? LOAD_ATTR_METHOD_WITH_VALUES : LOAD_ATTR_NONDESCRIPTOR_WITH_VALUES); } else {