@@ -47,6 +47,7 @@ print_stats(SpecializationStats *stats, const char *name)
4747 PRINT_STAT (name , deferred );
4848 PRINT_STAT (name , miss );
4949 PRINT_STAT (name , deopt );
50+ PRINT_STAT (name , unquickened );
5051#if SPECIALIZATION_STATS_DETAILED
5152 if (stats -> miss_types == NULL ) {
5253 return ;
@@ -302,6 +303,8 @@ _Py_Quicken(PyCodeObject *code) {
302303 return 0 ;
303304}
304305
306+
307+
305308static int
306309specialize_module_load_attr (
307310 PyObject * owner , _Py_CODEUNIT * instr , PyObject * name ,
@@ -349,6 +352,68 @@ specialize_module_load_attr(
349352 return 0 ;
350353}
351354
355+
356+
357+ /* Attribute specialization */
358+
359+ typedef enum {
360+ OVERRIDING , /* Is an overriding descriptor, and will remain so. */
361+ METHOD , /* Attribute has Py_TPFLAGS_METHOD_DESCRIPTOR set */
362+ PROPERTY , /* Is a property */
363+ OBJECT_SLOT , /* Is an object slot descriptor */
364+ OTHER_SLOT , /* Is a slot descriptor of another type */
365+ NON_OVERRIDING , /* Is another non-overriding descriptor, and is an instance of an immutable class*/
366+ NON_DESCRIPTOR , /* Is not a descriptor, and is an instance of an immutable class */
367+ MUTABLE , /* Instance of a mutable class; might, or might not, be a descriptor */
368+ ABSENT , /* Attribute is not present on the class */
369+ DUNDER_CLASS , /* __class__ attribute */
370+ GETATTRIBUTE_OVERRIDDEN /* __getattribute__ has been overridden */
371+ } DesciptorClassification ;
372+
373+ static DesciptorClassification
374+ analyze_descriptor (PyTypeObject * type , PyObject * name , PyObject * * descr )
375+ {
376+ if (type -> tp_getattro != PyObject_GenericGetAttr ) {
377+ * descr = NULL ;
378+ return GETATTRIBUTE_OVERRIDDEN ;
379+ }
380+ PyObject * descriptor = _PyType_Lookup (type , name );
381+ * descr = descriptor ;
382+ if (descriptor == NULL ) {
383+ return ABSENT ;
384+ }
385+ PyTypeObject * desc_cls = Py_TYPE (descriptor );
386+ if (!(desc_cls -> tp_flags & Py_TPFLAGS_IMMUTABLETYPE )) {
387+ return MUTABLE ;
388+ }
389+ if (desc_cls -> tp_descr_set ) {
390+ if (desc_cls == & PyMemberDescr_Type ) {
391+ PyMemberDescrObject * member = (PyMemberDescrObject * )descriptor ;
392+ struct PyMemberDef * dmem = member -> d_member ;
393+ if (dmem -> type == T_OBJECT_EX ) {
394+ return OBJECT_SLOT ;
395+ }
396+ return OTHER_SLOT ;
397+ }
398+ if (desc_cls == & PyProperty_Type ) {
399+ return PROPERTY ;
400+ }
401+ if (PyUnicode_CompareWithASCIIString (name , "__class__" ) == 0 ) {
402+ if (descriptor == _PyType_Lookup (& PyBaseObject_Type , name )) {
403+ return DUNDER_CLASS ;
404+ }
405+ }
406+ return OVERRIDING ;
407+ }
408+ if (desc_cls -> tp_descr_get ) {
409+ if (desc_cls -> tp_flags & Py_TPFLAGS_METHOD_DESCRIPTOR ) {
410+ return METHOD ;
411+ }
412+ return NON_OVERRIDING ;
413+ }
414+ return NON_DESCRIPTOR ;
415+ }
416+
352417int
353418_Py_Specialize_LoadAttr (PyObject * owner , _Py_CODEUNIT * instr , PyObject * name , SpecializedCacheEntry * cache )
354419{
@@ -362,94 +427,134 @@ _Py_Specialize_LoadAttr(PyObject *owner, _Py_CODEUNIT *instr, PyObject *name, Sp
362427 goto success ;
363428 }
364429 PyTypeObject * type = Py_TYPE (owner );
365- if (type -> tp_getattro != PyObject_GenericGetAttr ) {
366- SPECIALIZATION_FAIL (LOAD_ATTR , Py_TYPE (owner ), name , "__getattribute__ overridden" );
367- goto fail ;
368- }
369430 if (type -> tp_dict == NULL ) {
370431 if (PyType_Ready (type ) < 0 ) {
371432 return -1 ;
372433 }
373434 }
374- PyObject * descr = _PyType_Lookup (type , name );
375- if (descr != NULL ) {
376- // We found an attribute with a data-like descriptor.
377- PyTypeObject * dtype = Py_TYPE (descr );
378- if (dtype != & PyMemberDescr_Type ) {
379- SPECIALIZATION_FAIL (LOAD_ATTR , Py_TYPE (owner ), name , "not a member descriptor" );
435+ PyObject * descr ;
436+ DesciptorClassification kind = analyze_descriptor (type , name , & descr );
437+ switch (kind ) {
438+ case OVERRIDING :
439+ SPECIALIZATION_FAIL (LOAD_ATTR , type , name , "overriding descriptor" );
380440 goto fail ;
381- }
382- // It's a slot
383- PyMemberDescrObject * member = (PyMemberDescrObject * )descr ;
384- struct PyMemberDef * dmem = member -> d_member ;
385- if (dmem -> type != T_OBJECT_EX ) {
386- SPECIALIZATION_FAIL (LOAD_ATTR , Py_TYPE (owner ), name , "non-object slot" );
441+ case METHOD :
442+ SPECIALIZATION_FAIL (LOAD_ATTR , type , name , "method" );
387443 goto fail ;
388- }
389- Py_ssize_t offset = dmem -> offset ;
390- if (offset != (uint16_t )offset ) {
391- SPECIALIZATION_FAIL (LOAD_ATTR , Py_TYPE (owner ), name , "offset out of range" );
444+ case PROPERTY :
445+ SPECIALIZATION_FAIL (LOAD_ATTR , type , name , "property" );
392446 goto fail ;
447+ case OBJECT_SLOT :
448+ {
449+ PyMemberDescrObject * member = (PyMemberDescrObject * )descr ;
450+ struct PyMemberDef * dmem = member -> d_member ;
451+ Py_ssize_t offset = dmem -> offset ;
452+ if (offset != (uint16_t )offset ) {
453+ SPECIALIZATION_FAIL (LOAD_ATTR , type , name , "offset out of range" );
454+ goto fail ;
455+ }
456+ assert (dmem -> type == T_OBJECT_EX );
457+ assert (offset > 0 );
458+ cache0 -> index = (uint16_t )offset ;
459+ cache1 -> tp_version = type -> tp_version_tag ;
460+ * instr = _Py_MAKECODEUNIT (LOAD_ATTR_SLOT , _Py_OPARG (* instr ));
461+ goto success ;
393462 }
394- assert (offset > 0 );
395- cache0 -> index = (uint16_t )offset ;
396- cache1 -> tp_version = type -> tp_version_tag ;
397- * instr = _Py_MAKECODEUNIT (LOAD_ATTR_SLOT , _Py_OPARG (* instr ));
398- goto success ;
399- }
400- // No desciptor
401- if (type -> tp_dictoffset <= 0 ) {
402- SPECIALIZATION_FAIL (LOAD_ATTR , Py_TYPE (owner ), name , "no dict or negative offset" );
403- goto fail ;
404- }
405- PyObject * * dictptr = (PyObject * * ) ((char * )owner + type -> tp_dictoffset );
406- if (* dictptr == NULL || !PyDict_CheckExact (* dictptr )) {
407- SPECIALIZATION_FAIL (LOAD_ATTR , Py_TYPE (owner ), name , "no dict or not a dict" );
463+ case DUNDER_CLASS :
464+ {
465+ Py_ssize_t offset = offsetof(PyObject , ob_type );
466+ assert (offset == (uint16_t )offset );
467+ cache0 -> index = (uint16_t )offset ;
468+ cache1 -> tp_version = type -> tp_version_tag ;
469+ * instr = _Py_MAKECODEUNIT (LOAD_ATTR_SLOT , _Py_OPARG (* instr ));
470+ goto success ;
471+ }
472+ case OTHER_SLOT :
473+ SPECIALIZATION_FAIL (LOAD_ATTR , type , name , "non-object slot" );
474+ goto fail ;
475+ case MUTABLE :
476+ SPECIALIZATION_FAIL (LOAD_ATTR , type , name , "mutable class attribute" );
477+ goto fail ;
478+ case GETATTRIBUTE_OVERRIDDEN :
479+ SPECIALIZATION_FAIL (LOAD_ATTR , type , name , "__getattribute__ overridden" );
480+ goto fail ;
481+ case NON_OVERRIDING :
482+ case NON_DESCRIPTOR :
483+ case ABSENT :
484+ break ;
485+ }
486+ assert (kind == NON_OVERRIDING || kind == NON_DESCRIPTOR || kind == ABSENT );
487+ // No desciptor, or non overriding.
488+ if (type -> tp_dictoffset < 0 ) {
489+ SPECIALIZATION_FAIL (LOAD_ATTR , type , name , "negative offset" );
408490 goto fail ;
409491 }
410- // We found an instance with a __dict__.
411- PyDictObject * dict = (PyDictObject * )* dictptr ;
412- if ((type -> tp_flags & Py_TPFLAGS_HEAPTYPE )
413- && dict -> ma_keys == ((PyHeapTypeObject * )type )-> ht_cached_keys
414- ) {
415- // Keys are shared
416- assert (PyUnicode_CheckExact (name ));
417- Py_hash_t hash = PyObject_Hash (name );
418- if (hash == -1 ) {
419- return -1 ;
420- }
421- PyObject * value ;
422- Py_ssize_t index = _Py_dict_lookup (dict , name , hash , & value );
423- assert (index != DKIX_ERROR );
424- if (index != (uint16_t )index ) {
425- SPECIALIZATION_FAIL (LOAD_ATTR , Py_TYPE (owner ), name , "index out of range" );
492+ if (type -> tp_dictoffset > 0 ) {
493+ PyObject * * dictptr = (PyObject * * ) ((char * )owner + type -> tp_dictoffset );
494+ if (* dictptr == NULL || !PyDict_CheckExact (* dictptr )) {
495+ SPECIALIZATION_FAIL (LOAD_ATTR , type , name , "no dict or not a dict" );
426496 goto fail ;
427497 }
428- uint32_t keys_version = _PyDictKeys_GetVersionForCurrentState (dict );
429- if (keys_version == 0 ) {
430- SPECIALIZATION_FAIL (LOAD_ATTR , Py_TYPE (owner ), name , "no more key versions" );
431- goto fail ;
498+ // We found an instance with a __dict__.
499+ PyDictObject * dict = (PyDictObject * )* dictptr ;
500+ if ((type -> tp_flags & Py_TPFLAGS_HEAPTYPE )
501+ && dict -> ma_keys == ((PyHeapTypeObject * )type )-> ht_cached_keys
502+ ) {
503+ // Keys are shared
504+ assert (PyUnicode_CheckExact (name ));
505+ Py_hash_t hash = PyObject_Hash (name );
506+ if (hash == -1 ) {
507+ return -1 ;
508+ }
509+ PyObject * value ;
510+ Py_ssize_t index = _Py_dict_lookup (dict , name , hash , & value );
511+ assert (index != DKIX_ERROR );
512+ if (index != (uint16_t )index ) {
513+ SPECIALIZATION_FAIL (LOAD_ATTR , type , name ,
514+ index < 0 ? "attribute not in dict" : "index out of range" );
515+ goto fail ;
516+ }
517+ uint32_t keys_version = _PyDictKeys_GetVersionForCurrentState (dict );
518+ if (keys_version == 0 ) {
519+ SPECIALIZATION_FAIL (LOAD_ATTR , type , name , "no more key versions" );
520+ goto fail ;
521+ }
522+ cache1 -> dk_version_or_hint = keys_version ;
523+ cache1 -> tp_version = type -> tp_version_tag ;
524+ cache0 -> index = (uint16_t )index ;
525+ * instr = _Py_MAKECODEUNIT (LOAD_ATTR_SPLIT_KEYS , _Py_OPARG (* instr ));
526+ goto success ;
527+ }
528+ else {
529+ PyObject * value = NULL ;
530+ Py_ssize_t hint =
531+ _PyDict_GetItemHint (dict , name , -1 , & value );
532+ if (hint != (uint32_t )hint ) {
533+ SPECIALIZATION_FAIL (LOAD_ATTR , type , name , "hint out of range" );
534+ goto fail ;
535+ }
536+ cache1 -> dk_version_or_hint = (uint32_t )hint ;
537+ cache1 -> tp_version = type -> tp_version_tag ;
538+ * instr = _Py_MAKECODEUNIT (LOAD_ATTR_WITH_HINT , _Py_OPARG (* instr ));
539+ goto success ;
432540 }
433- cache1 -> dk_version_or_hint = keys_version ;
434- cache1 -> tp_version = type -> tp_version_tag ;
435- cache0 -> index = (uint16_t )index ;
436- * instr = _Py_MAKECODEUNIT (LOAD_ATTR_SPLIT_KEYS , _Py_OPARG (* instr ));
437- goto success ;
438541 }
439- else {
440- PyObject * value = NULL ;
441- Py_ssize_t hint =
442- _PyDict_GetItemHint (dict , name , -1 , & value );
443- if (hint != (uint32_t )hint ) {
444- SPECIALIZATION_FAIL (LOAD_ATTR , Py_TYPE (owner ), name , "hint out of range" );
542+ assert (type -> tp_dictoffset == 0 );
543+ /* No attribute in instance dictionary */
544+ switch (kind ) {
545+ case NON_OVERRIDING :
546+ SPECIALIZATION_FAIL (LOAD_ATTR , type , name , "non-overriding descriptor" );
445547 goto fail ;
446- }
447- cache1 -> dk_version_or_hint = (uint32_t )hint ;
448- cache1 -> tp_version = type -> tp_version_tag ;
449- * instr = _Py_MAKECODEUNIT (LOAD_ATTR_WITH_HINT , _Py_OPARG (* instr ));
450- goto success ;
548+ case NON_DESCRIPTOR :
549+ /* To do -- Optimize this case */
550+ SPECIALIZATION_FAIL (LOAD_ATTR , type , name , "non descriptor" );
551+ goto fail ;
552+ case ABSENT :
553+ SPECIALIZATION_FAIL (LOAD_ATTR , type , name , "no attribute" );
554+ goto fail ;
555+ default :
556+ Py_UNREACHABLE ();
451557 }
452-
453558fail :
454559 STAT_INC (LOAD_ATTR , specialization_failure );
455560 assert (!PyErr_Occurred ());
@@ -462,7 +567,6 @@ _Py_Specialize_LoadAttr(PyObject *owner, _Py_CODEUNIT *instr, PyObject *name, Sp
462567 return 0 ;
463568}
464569
465-
466570int
467571_Py_Specialize_LoadGlobal (
468572 PyObject * globals , PyObject * builtins ,
0 commit comments