@@ -748,13 +748,12 @@ specialize_attr_loadclassattr(PyObject *owner, _Py_CODEUNIT *instr,
748748 PyObject * name , PyObject * descr ,
749749 unsigned int tp_version ,
750750 DescriptorClassification kind , bool is_method ,
751- uint32_t shared_keys_version ,
752- uint16_t slot_offset );
751+ uint32_t shared_keys_version );
753752static int specialize_class_load_attr (PyObject * owner , _Py_CODEUNIT * instr , PyObject * name );
754753
755- /* Returns true if instances of obj's class are
756- * likely to have `name` in their __dict__.
757- * For objects with inline values, we check in the shared keys .
754+ /* Returns true if obj may have its own value for `name`.
755+ * For objects with inline values, we check obj's slot for `name` in the
756+ * shared keys: it may be empty even though the name has a slot .
758757 * For other objects, we check their actual dictionary.
759758 */
760759static bool
@@ -768,7 +767,17 @@ instance_has_key(PyObject *obj, PyObject *name, uint32_t *shared_keys_version)
768767 PyDictKeysObject * keys = ((PyHeapTypeObject * )cls )-> ht_cached_keys ;
769768 Py_ssize_t index =
770769 _PyDictKeys_StringLookupAndVersion (keys , name , shared_keys_version );
771- return index >= 0 ;
770+ if (index < 0 ) {
771+ return false;
772+ }
773+ bool result ;
774+ Py_BEGIN_CRITICAL_SECTION (obj );
775+ PyDictValues * values = _PyObject_InlineValues (obj );
776+ result = !FT_ATOMIC_LOAD_UINT8 (values -> valid ) ||
777+ index >= values -> capacity ||
778+ FT_ATOMIC_LOAD_PTR_RELAXED (values -> values [index ]) != NULL ;
779+ Py_END_CRITICAL_SECTION ();
780+ return result ;
772781 }
773782 PyDictObject * dict = _PyObject_GetManagedDict (obj );
774783 if (dict == NULL || !PyDict_CheckExact (dict )) {
@@ -786,45 +795,9 @@ instance_has_key(PyObject *obj, PyObject *name, uint32_t *shared_keys_version)
786795 return result ;
787796}
788797
789- /* Returns true if `owner`'s inline slot for `name` (an entry in the class's
790- * shared keys) holds a value, or if there is no inline slot a specialized
791- * instruction could check. Returns false if the slot is empty, and stores
792- * its byte offset from the start of the object in *empty_slot_offset. An
793- * empty slot means the attribute is not set on this instance even though the
794- * class's shared keys have an entry for it (e.g. from __static_attributes__),
795- * so a load finds the class attribute.
796- */
797- static bool
798- inline_slot_is_set (PyObject * owner , PyObject * name , uint16_t * empty_slot_offset )
799- {
800- PyTypeObject * type = Py_TYPE (owner );
801- if (!(type -> tp_flags & Py_TPFLAGS_INLINE_VALUES )) {
802- return true;
803- }
804- bool set = true;
805- Py_BEGIN_CRITICAL_SECTION (owner );
806- PyDictValues * values = _PyObject_InlineValues (owner );
807- if (FT_ATOMIC_LOAD_UINT8 (values -> valid )) {
808- PyDictKeysObject * keys = ((PyHeapTypeObject * )type )-> ht_cached_keys ;
809- Py_ssize_t index = _PyDictKeys_StringLookupSplit (keys , name );
810- if (index >= 0 && index < values -> capacity &&
811- FT_ATOMIC_LOAD_PTR_RELAXED (values -> values [index ]) == NULL )
812- {
813- Py_ssize_t slot = (char * )& values -> values [index ] - (char * )owner ;
814- if (slot == (uint16_t )slot ) {
815- * empty_slot_offset = (uint16_t )slot ;
816- set = false;
817- }
818- }
819- }
820- Py_END_CRITICAL_SECTION ();
821- return set ;
822- }
823-
824798static int
825799do_specialize_instance_load_attr (PyObject * owner , _Py_CODEUNIT * instr , PyObject * name ,
826800 bool shadow , uint32_t shared_keys_version ,
827- uint16_t empty_slot_offset ,
828801 DescriptorClassification kind , PyObject * descr , unsigned int tp_version )
829802{
830803 _PyAttrCache * cache = (_PyAttrCache * )(instr + 1 );
@@ -846,8 +819,7 @@ do_specialize_instance_load_attr(PyObject* owner, _Py_CODEUNIT* instr, PyObject*
846819 if (oparg & 1 ) {
847820 if (specialize_attr_loadclassattr (owner , instr , name , descr ,
848821 tp_version , kind , true,
849- shared_keys_version ,
850- empty_slot_offset )) {
822+ shared_keys_version )) {
851823 return 0 ;
852824 }
853825 else {
@@ -993,8 +965,7 @@ do_specialize_instance_load_attr(PyObject* owner, _Py_CODEUNIT* instr, PyObject*
993965 if ((oparg & 1 ) == 0 ) {
994966 if (specialize_attr_loadclassattr (owner , instr , name , descr ,
995967 tp_version , kind , false,
996- shared_keys_version ,
997- empty_slot_offset )) {
968+ shared_keys_version )) {
998969 return 0 ;
999970 }
1000971 }
@@ -1028,14 +999,8 @@ specialize_instance_load_attr(PyObject* owner, _Py_CODEUNIT* instr, PyObject* na
1028999 // then the type version, this ensures we will still deopt if that happens.
10291000 DescriptorClassification kind = analyze_descriptor_load (type , name , & descr , & tp_version );
10301001 uint32_t shared_keys_version = 0 ;
1031- uint16_t empty_slot_offset = 0 ;
1032- /* True if this instance has its own value for the name. For inline
1033- * values, the name being in the shared keys is not enough: the slot may
1034- * be empty, and then the load finds the class attribute. */
1035- bool shadow = instance_has_key (owner , name , & shared_keys_version ) &&
1036- inline_slot_is_set (owner , name , & empty_slot_offset );
1037- int result = do_specialize_instance_load_attr (owner , instr , name , shadow , shared_keys_version ,
1038- empty_slot_offset , kind , descr , tp_version );
1002+ bool shadow = instance_has_key (owner , name , & shared_keys_version );
1003+ int result = do_specialize_instance_load_attr (owner , instr , name , shadow , shared_keys_version , kind , descr , tp_version );
10391004 Py_XDECREF (descr );
10401005 return result ;
10411006}
@@ -1310,8 +1275,7 @@ specialize_attr_loadclassattr(PyObject *owner, _Py_CODEUNIT *instr,
13101275 PyObject * name , PyObject * descr ,
13111276 unsigned int tp_version ,
13121277 DescriptorClassification kind , bool is_method ,
1313- uint32_t shared_keys_version ,
1314- uint16_t slot_offset )
1278+ uint32_t shared_keys_version )
13151279{
13161280 _PyLoadMethodCache * cache = (_PyLoadMethodCache * )(instr + 1 );
13171281 PyTypeObject * owner_cls = Py_TYPE (owner );
@@ -1328,14 +1292,27 @@ specialize_attr_loadclassattr(PyObject *owner, _Py_CODEUNIT *instr,
13281292
13291293 unsigned long tp_flags = PyType_GetFlags (owner_cls );
13301294 if (tp_flags & Py_TPFLAGS_INLINE_VALUES ) {
1331- #ifndef Py_GIL_DISABLED
1332- assert (slot_offset != 0 || _PyDictKeys_StringLookup (
1333- ((PyHeapTypeObject * )owner_cls )-> ht_cached_keys , name ) < 0 );
1334- #endif
13351295 if (shared_keys_version == 0 ) {
13361296 SPECIALIZATION_FAIL (LOAD_ATTR , SPEC_FAIL_OUT_OF_VERSIONS );
13371297 return 0 ;
13381298 }
1299+ /* The name can have a slot in the shared keys (e.g. from
1300+ * __static_attributes__) that is empty in this instance. Store the
1301+ * slot's offset so the instruction can check it stays empty; 0 means
1302+ * there is no slot to check. */
1303+ uint16_t slot_offset = 0 ;
1304+ Py_ssize_t index = _PyDictKeys_StringLookupSplit (
1305+ ((PyHeapTypeObject * )owner_cls )-> ht_cached_keys , name );
1306+ assert (index != DKIX_ERROR );
1307+ if (index >= 0 ) {
1308+ char * value_addr = (char * )& _PyObject_InlineValues (owner )-> values [index ];
1309+ Py_ssize_t offset = value_addr - (char * )owner ;
1310+ if (offset != (uint16_t )offset ) {
1311+ SPECIALIZATION_FAIL (LOAD_ATTR , SPEC_FAIL_OUT_OF_RANGE );
1312+ return 0 ;
1313+ }
1314+ slot_offset = (uint16_t )offset ;
1315+ }
13391316 cache -> keys_version [0 ] = slot_offset ;
13401317 specialize (instr , is_method ? LOAD_ATTR_METHOD_WITH_VALUES : LOAD_ATTR_NONDESCRIPTOR_WITH_VALUES );
13411318 }
0 commit comments