summaryrefslogtreecommitdiffstats
path: root/Python/ceval.c
diff options
context:
space:
mode:
authorMark Shannon <mark@hotpy.org>2021-10-13 13:19:34 (GMT)
committerGitHub <noreply@github.com>2021-10-13 13:19:34 (GMT)
commita8b9350964f43cb648c98c179c8037fbf3ff8a7d (patch)
tree13a539432c9d48ac278d34d040f17a7a12eac771 /Python/ceval.c
parent97308dfcdc0696e0b116c37386e2ff4d72e6c3f4 (diff)
downloadcpython-a8b9350964f43cb648c98c179c8037fbf3ff8a7d.zip
cpython-a8b9350964f43cb648c98c179c8037fbf3ff8a7d.tar.gz
cpython-a8b9350964f43cb648c98c179c8037fbf3ff8a7d.tar.bz2
bpo-45340: Don't create object dictionaries unless actually needed (GH-28802)
* Never change types' cached keys. It could invalidate inline attribute objects. * Lazily create object dictionaries. * Update specialization of LOAD/STORE_ATTR. * Don't update shared keys version for deletion of value. * Update gdb support to handle instance values. * Rename SPLIT_KEYS opcodes to INSTANCE_VALUE.
Diffstat (limited to 'Python/ceval.c')
-rw-r--r--Python/ceval.c72
1 files changed, 36 insertions, 36 deletions
diff --git a/Python/ceval.c b/Python/ceval.c
index f4cacd8..5cf2ab3 100644
--- a/Python/ceval.c
+++ b/Python/ceval.c
@@ -3618,7 +3618,7 @@ check_eval_breaker:
}
}
- TARGET(LOAD_ATTR_SPLIT_KEYS) {
+ TARGET(LOAD_ATTR_INSTANCE_VALUE) {
assert(cframe.use_tracing == 0);
PyObject *owner = TOP();
PyObject *res;
@@ -3629,11 +3629,10 @@ check_eval_breaker:
assert(cache1->tp_version != 0);
DEOPT_IF(tp->tp_version_tag != cache1->tp_version, LOAD_ATTR);
assert(tp->tp_dictoffset > 0);
- PyDictObject *dict = *(PyDictObject **)(((char *)owner) + tp->tp_dictoffset);
- DEOPT_IF(dict == NULL, LOAD_ATTR);
- assert(PyDict_CheckExact((PyObject *)dict));
- DEOPT_IF(dict->ma_keys->dk_version != cache1->dk_version_or_hint, LOAD_ATTR);
- res = dict->ma_values->values[cache0->index];
+ assert(tp->tp_inline_values_offset > 0);
+ PyDictValues *values = *(PyDictValues **)(((char *)owner) + tp->tp_inline_values_offset);
+ DEOPT_IF(values == NULL, LOAD_ATTR);
+ res = values->values[cache0->index];
DEOPT_IF(res == NULL, LOAD_ATTR);
STAT_INC(LOAD_ATTR, hit);
record_cache_hit(cache0);
@@ -3725,7 +3724,7 @@ check_eval_breaker:
}
}
- TARGET(STORE_ATTR_SPLIT_KEYS) {
+ TARGET(STORE_ATTR_INSTANCE_VALUE) {
assert(cframe.use_tracing == 0);
PyObject *owner = TOP();
PyTypeObject *tp = Py_TYPE(owner);
@@ -3735,31 +3734,23 @@ check_eval_breaker:
assert(cache1->tp_version != 0);
DEOPT_IF(tp->tp_version_tag != cache1->tp_version, STORE_ATTR);
assert(tp->tp_dictoffset > 0);
- PyDictObject *dict = *(PyDictObject **)(((char *)owner) + tp->tp_dictoffset);
- DEOPT_IF(dict == NULL, STORE_ATTR);
- assert(PyDict_CheckExact((PyObject *)dict));
- DEOPT_IF(dict->ma_keys->dk_version != cache1->dk_version_or_hint, STORE_ATTR);
+ assert(tp->tp_inline_values_offset > 0);
+ PyDictValues *values = *(PyDictValues **)(((char *)owner) + tp->tp_inline_values_offset);
+ DEOPT_IF(values == NULL, STORE_ATTR);
STAT_INC(STORE_ATTR, hit);
record_cache_hit(cache0);
int index = cache0->index;
STACK_SHRINK(1);
PyObject *value = POP();
- PyObject *old_value = dict->ma_values->values[index];
- dict->ma_values->values[index] = value;
+ PyObject *old_value = values->values[index];
+ values->values[index] = value;
if (old_value == NULL) {
assert(index < 16);
- dict->ma_values->mv_order = (dict->ma_values->mv_order << 4) | index;
- dict->ma_used++;
+ values->mv_order = (values->mv_order << 4) | index;
}
else {
Py_DECREF(old_value);
}
- /* Ensure dict is GC tracked if it needs to be */
- if (!_PyObject_GC_IS_TRACKED(dict) && _PyObject_GC_MAY_BE_TRACKED(value)) {
- _PyObject_GC_TRACK(dict);
- }
- /* PEP 509 */
- dict->ma_version_tag = DICT_NEXT_VERSION();
Py_DECREF(owner);
DISPATCH();
}
@@ -4474,21 +4465,31 @@ check_eval_breaker:
_PyObjectCache *cache2 = &caches[-2].obj;
DEOPT_IF(self_cls->tp_version_tag != cache1->tp_version, LOAD_METHOD);
- assert(cache1->dk_version_or_hint != 0);
- assert(cache1->tp_version != 0);
- assert(self_cls->tp_dictoffset >= 0);
- assert(Py_TYPE(self_cls)->tp_dictoffset > 0);
-
- // inline version of _PyObject_GetDictPtr for offset >= 0
- PyObject *dict = self_cls->tp_dictoffset != 0 ?
- *(PyObject **) ((char *)self + self_cls->tp_dictoffset) : NULL;
-
- // Ensure self.__dict__ didn't modify keys.
- // Don't care if self has no dict, it could be builtin or __slots__.
- DEOPT_IF(dict != NULL &&
- ((PyDictObject *)dict)->ma_keys->dk_version !=
- cache1->dk_version_or_hint, LOAD_METHOD);
+ assert(self_cls->tp_dictoffset > 0);
+ assert(self_cls->tp_inline_values_offset > 0);
+ PyDictObject *dict = *(PyDictObject **)(((char *)self) + self_cls->tp_dictoffset);
+ DEOPT_IF(dict != NULL, LOAD_METHOD);
+ DEOPT_IF(((PyHeapTypeObject *)self_cls)->ht_cached_keys->dk_version != cache1->dk_version_or_hint, LOAD_METHOD);
+ STAT_INC(LOAD_METHOD, hit);
+ record_cache_hit(cache0);
+ PyObject *res = cache2->obj;
+ assert(res != NULL);
+ assert(_PyType_HasFeature(Py_TYPE(res), Py_TPFLAGS_METHOD_DESCRIPTOR));
+ Py_INCREF(res);
+ SET_TOP(res);
+ PUSH(self);
+ DISPATCH();
+ }
+ TARGET(LOAD_METHOD_NO_DICT) {
+ PyObject *self = TOP();
+ PyTypeObject *self_cls = Py_TYPE(self);
+ SpecializedCacheEntry *caches = GET_CACHE();
+ _PyAdaptiveEntry *cache0 = &caches[0].adaptive;
+ _PyAttrCache *cache1 = &caches[-1].attr;
+ _PyObjectCache *cache2 = &caches[-2].obj;
+ DEOPT_IF(self_cls->tp_version_tag != cache1->tp_version, LOAD_METHOD);
+ assert(self_cls->tp_dictoffset == 0);
STAT_INC(LOAD_METHOD, hit);
record_cache_hit(cache0);
PyObject *res = cache2->obj;
@@ -4530,7 +4531,6 @@ check_eval_breaker:
record_cache_hit(cache0);
PyObject *res = cache2->obj;
assert(res != NULL);
- assert(_PyType_HasFeature(Py_TYPE(res), Py_TPFLAGS_METHOD_DESCRIPTOR));
Py_INCREF(res);
SET_TOP(NULL);
Py_DECREF(cls);