Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 76efcb4

Browse files
authored
GH-100288: Skip extra work when failing to specialize LOAD_ATTR (GH-101354)
1 parent 2753cf2 commit 76efcb4

File tree

1 file changed

+24
-60
lines changed

1 file changed

+24
-60
lines changed

Python/specialize.c

Lines changed: 24 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -1039,14 +1039,6 @@ specialize_class_load_attr(PyObject *owner, _Py_CODEUNIT *instr,
10391039
}
10401040
}
10411041

1042-
typedef enum {
1043-
MANAGED_VALUES = 1,
1044-
MANAGED_DICT = 2,
1045-
OFFSET_DICT = 3,
1046-
NO_DICT = 4,
1047-
LAZY_DICT = 5,
1048-
} ObjectDictKind;
1049-
10501042
// Please collect stats carefully before and after modifying. A subtle change
10511043
// can cause a significant drop in cache hits. A possible test is
10521044
// python.exe -m test_typing test_re test_dis test_zlib.
@@ -1058,71 +1050,45 @@ PyObject *descr, DescriptorClassification kind)
10581050
PyTypeObject *owner_cls = Py_TYPE(owner);
10591051

10601052
assert(kind == METHOD && descr != NULL);
1061-
ObjectDictKind dictkind;
1062-
PyDictKeysObject *keys;
10631053
if (owner_cls->tp_flags & Py_TPFLAGS_MANAGED_DICT) {
10641054
PyDictOrValues dorv = *_PyObject_DictOrValuesPointer(owner);
1065-
keys = ((PyHeapTypeObject *)owner_cls)->ht_cached_keys;
1066-
if (_PyDictOrValues_IsValues(dorv)) {
1067-
dictkind = MANAGED_VALUES;
1068-
}
1069-
else {
1070-
dictkind = MANAGED_DICT;
1071-
}
1072-
}
1073-
else {
1074-
Py_ssize_t dictoffset = owner_cls->tp_dictoffset;
1075-
if (dictoffset < 0 || dictoffset > INT16_MAX) {
1076-
SPECIALIZATION_FAIL(LOAD_ATTR, SPEC_FAIL_OUT_OF_RANGE);
1077-
goto fail;
1078-
}
1079-
if (dictoffset == 0) {
1080-
dictkind = NO_DICT;
1081-
keys = NULL;
1082-
}
1083-
else {
1084-
PyObject *dict = *(PyObject **) ((char *)owner + dictoffset);
1085-
if (dict == NULL) {
1086-
// This object will have a dict if user access __dict__
1087-
dictkind = LAZY_DICT;
1088-
keys = NULL;
1089-
}
1090-
else {
1091-
keys = ((PyDictObject *)dict)->ma_keys;
1092-
dictkind = OFFSET_DICT;
1093-
}
1055+
PyDictKeysObject *keys = ((PyHeapTypeObject *)owner_cls)->ht_cached_keys;
1056+
if (!_PyDictOrValues_IsValues(dorv)) {
1057+
SPECIALIZATION_FAIL(LOAD_ATTR, SPEC_FAIL_ATTR_HAS_MANAGED_DICT);
1058+
return 0;
10941059
}
1095-
}
1096-
if (dictkind == MANAGED_VALUES || dictkind == OFFSET_DICT) {
10971060
Py_ssize_t index = _PyDictKeys_StringLookup(keys, name);
10981061
if (index != DKIX_EMPTY) {
10991062
SPECIALIZATION_FAIL(LOAD_ATTR, SPEC_FAIL_ATTR_SHADOWED);
1100-
goto fail;
1063+
return 0;
11011064
}
11021065
uint32_t keys_version = _PyDictKeys_GetVersionForCurrentState(keys);
11031066
if (keys_version == 0) {
11041067
SPECIALIZATION_FAIL(LOAD_ATTR, SPEC_FAIL_OUT_OF_VERSIONS);
1105-
goto fail;
1068+
return 0;
11061069
}
11071070
write_u32(cache->keys_version, keys_version);
1071+
_py_set_opcode(instr, LOAD_ATTR_METHOD_WITH_VALUES);
11081072
}
1109-
switch(dictkind) {
1110-
case NO_DICT:
1073+
else {
1074+
Py_ssize_t dictoffset = owner_cls->tp_dictoffset;
1075+
if (dictoffset < 0 || dictoffset > INT16_MAX) {
1076+
SPECIALIZATION_FAIL(LOAD_ATTR, SPEC_FAIL_OUT_OF_RANGE);
1077+
return 0;
1078+
}
1079+
if (dictoffset == 0) {
11111080
_py_set_opcode(instr, LOAD_ATTR_METHOD_NO_DICT);
1112-
break;
1113-
case MANAGED_VALUES:
1114-
_py_set_opcode(instr, LOAD_ATTR_METHOD_WITH_VALUES);
1115-
break;
1116-
case MANAGED_DICT:
1117-
SPECIALIZATION_FAIL(LOAD_ATTR, SPEC_FAIL_ATTR_HAS_MANAGED_DICT);
1118-
goto fail;
1119-
case OFFSET_DICT:
1120-
SPECIALIZATION_FAIL(LOAD_ATTR, SPEC_FAIL_ATTR_NOT_MANAGED_DICT);
1121-
goto fail;
1122-
case LAZY_DICT:
1123-
assert(owner_cls->tp_dictoffset > 0 && owner_cls->tp_dictoffset <= INT16_MAX);
1081+
}
1082+
else {
1083+
PyObject *dict = *(PyObject **) ((char *)owner + dictoffset);
1084+
if (dict) {
1085+
SPECIALIZATION_FAIL(LOAD_ATTR, SPEC_FAIL_ATTR_NOT_MANAGED_DICT);
1086+
return 0;
1087+
}
1088+
assert(owner_cls->tp_dictoffset > 0);
1089+
assert(owner_cls->tp_dictoffset <= INT16_MAX);
11241090
_py_set_opcode(instr, LOAD_ATTR_METHOD_LAZY_DICT);
1125-
break;
1091+
}
11261092
}
11271093
/* `descr` is borrowed. This is safe for methods (even inherited ones from
11281094
* super classes!) as long as tp_version_tag is validated for two main reasons:
@@ -1141,8 +1107,6 @@ PyObject *descr, DescriptorClassification kind)
11411107
write_u32(cache->type_version, owner_cls->tp_version_tag);
11421108
write_obj(cache->descr, descr);
11431109
return 1;
1144-
fail:
1145-
return 0;
11461110
}
11471111

11481112
void

0 commit comments

Comments
 (0)