summaryrefslogtreecommitdiffstats
path: root/Python/ceval.c
diff options
context:
space:
mode:
authorKen Jin <28750310+Fidget-Spinner@users.noreply.github.com>2021-08-17 14:55:55 (GMT)
committerGitHub <noreply@github.com>2021-08-17 14:55:55 (GMT)
commit96346cb6d0593ef9ec122614347ccb053cd63433 (patch)
tree9f2b952134803d5de5229ece709b19e29ef0e7ab /Python/ceval.c
parentfcd651d16fc5ac3d07dd3f57f1001a861a2e7d23 (diff)
downloadcpython-96346cb6d0593ef9ec122614347ccb053cd63433.zip
cpython-96346cb6d0593ef9ec122614347ccb053cd63433.tar.gz
cpython-96346cb6d0593ef9ec122614347ccb053cd63433.tar.bz2
bpo-44889: Specialize LOAD_METHOD with PEP 659 adaptive interpreter (GH-27722)
Adds four new instructions: * LOAD_METHOD_ADAPTIVE * LOAD_METHOD_CACHED * LOAD_METHOD_MODULE * LOAD_METHOD_CLASS
Diffstat (limited to 'Python/ceval.c')
-rw-r--r--Python/ceval.c142
1 files changed, 127 insertions, 15 deletions
diff --git a/Python/ceval.c b/Python/ceval.c
index 4878749..333c54f 100644
--- a/Python/ceval.c
+++ b/Python/ceval.c
@@ -1439,6 +1439,27 @@ eval_frame_handle_pending(PyThreadState *tstate)
#define BUILTINS() frame->f_builtins
#define LOCALS() frame->f_locals
+/* Shared opcode macros */
+
+// shared by LOAD_ATTR_MODULE and LOAD_METHOD_MODULE
+#define LOAD_MODULE_ATTR_OR_METHOD(attr_or_method) \
+ SpecializedCacheEntry *caches = GET_CACHE(); \
+ _PyAdaptiveEntry *cache0 = &caches[0].adaptive; \
+ _PyAttrCache *cache1 = &caches[-1].attr; \
+ DEOPT_IF(!PyModule_CheckExact(owner), LOAD_##attr_or_method); \
+ PyDictObject *dict = (PyDictObject *)((PyModuleObject *)owner)->md_dict; \
+ assert(dict != NULL); \
+ DEOPT_IF(dict->ma_keys->dk_version != cache1->dk_version_or_hint, \
+ LOAD_##attr_or_method); \
+ assert(dict->ma_keys->dk_kind == DICT_KEYS_UNICODE); \
+ assert(cache0->index < dict->ma_keys->dk_nentries); \
+ PyDictKeyEntry *ep = DK_ENTRIES(dict->ma_keys) + cache0->index; \
+ res = ep->me_value; \
+ DEOPT_IF(res == NULL, LOAD_##attr_or_method); \
+ STAT_INC(LOAD_##attr_or_method, hit); \
+ record_cache_hit(cache0); \
+ Py_INCREF(res);
+
static int
trace_function_entry(PyThreadState *tstate, InterpreterFrame *frame)
{
@@ -3511,23 +3532,10 @@ check_eval_breaker:
TARGET(LOAD_ATTR_MODULE): {
assert(cframe.use_tracing == 0);
+ // shared with LOAD_METHOD_MODULE
PyObject *owner = TOP();
PyObject *res;
- SpecializedCacheEntry *caches = GET_CACHE();
- _PyAdaptiveEntry *cache0 = &caches[0].adaptive;
- _PyAttrCache *cache1 = &caches[-1].attr;
- DEOPT_IF(!PyModule_CheckExact(owner), LOAD_ATTR);
- PyDictObject *dict = (PyDictObject *)((PyModuleObject *)owner)->md_dict;
- assert(dict != NULL);
- DEOPT_IF(dict->ma_keys->dk_version != cache1->dk_version_or_hint, LOAD_ATTR);
- assert(dict->ma_keys->dk_kind == DICT_KEYS_UNICODE);
- assert(cache0->index < dict->ma_keys->dk_nentries);
- PyDictKeyEntry *ep = DK_ENTRIES(dict->ma_keys) + cache0->index;
- res = ep->me_value;
- DEOPT_IF(res == NULL, LOAD_ATTR);
- STAT_INC(LOAD_ATTR, hit);
- record_cache_hit(cache0);
- Py_INCREF(res);
+ LOAD_MODULE_ATTR_OR_METHOD(ATTR);
SET_TOP(res);
Py_DECREF(owner);
DISPATCH();
@@ -4282,6 +4290,8 @@ check_eval_breaker:
}
TARGET(LOAD_METHOD): {
+ PREDICTED(LOAD_METHOD);
+ STAT_INC(LOAD_METHOD, unquickened);
/* Designed to work in tandem with CALL_METHOD. */
PyObject *name = GETITEM(names, oparg);
PyObject *obj = TOP();
@@ -4318,6 +4328,107 @@ check_eval_breaker:
DISPATCH();
}
+ TARGET(LOAD_METHOD_ADAPTIVE): {
+ assert(cframe.use_tracing == 0);
+ SpecializedCacheEntry *cache = GET_CACHE();
+ if (cache->adaptive.counter == 0) {
+ PyObject *owner = TOP();
+ PyObject *name = GETITEM(names, cache->adaptive.original_oparg);
+ next_instr--;
+ if (_Py_Specialize_LoadMethod(owner, next_instr, name, cache) < 0) {
+ goto error;
+ }
+ DISPATCH();
+ }
+ else {
+ STAT_INC(LOAD_METHOD, deferred);
+ cache->adaptive.counter--;
+ oparg = cache->adaptive.original_oparg;
+ STAT_DEC(LOAD_METHOD, unquickened);
+ JUMP_TO_INSTRUCTION(LOAD_METHOD);
+ }
+ }
+
+ TARGET(LOAD_METHOD_CACHED): {
+ /* LOAD_METHOD, with cached method object */
+ assert(cframe.use_tracing == 0);
+ 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(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);
+
+ 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_MODULE): {
+ assert(cframe.use_tracing == 0);
+ PyObject *owner = TOP();
+ PyObject *res;
+ LOAD_MODULE_ATTR_OR_METHOD(METHOD);
+ SET_TOP(NULL);
+ Py_DECREF(owner);
+ PUSH(res);
+ DISPATCH();
+ }
+
+ TARGET(LOAD_METHOD_CLASS): {
+ /* LOAD_METHOD, for class methods */
+ assert(cframe.use_tracing == 0);
+ SpecializedCacheEntry *caches = GET_CACHE();
+ _PyAdaptiveEntry *cache0 = &caches[0].adaptive;
+ _PyAttrCache *cache1 = &caches[-1].attr;
+ _PyObjectCache *cache2 = &caches[-2].obj;
+
+ PyObject *cls = TOP();
+ PyTypeObject *cls_type = Py_TYPE(cls);
+ assert(cls_type->tp_dictoffset > 0);
+ PyObject *dict = *(PyObject **) ((char *)cls + cls_type->tp_dictoffset);
+ // Don't care if no dict -- tp_version_tag should catch anything wrong.
+ DEOPT_IF(dict != NULL && ((PyDictObject *)dict)->ma_keys->dk_version !=
+ cache1->dk_version_or_hint, LOAD_METHOD);
+ DEOPT_IF(((PyTypeObject *)cls)->tp_version_tag != cache1->tp_version,
+ LOAD_METHOD);
+ assert(cache1->dk_version_or_hint != 0);
+ assert(cache1->tp_version != 0);
+
+ 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(NULL);
+ Py_DECREF(cls);
+ PUSH(res);
+ DISPATCH();
+ }
+
TARGET(CALL_METHOD): {
/* Designed to work in tamdem with LOAD_METHOD. */
PyObject **sp, *res;
@@ -4648,6 +4759,7 @@ opname ## _miss: \
MISS_WITH_CACHE(LOAD_ATTR)
MISS_WITH_CACHE(STORE_ATTR)
MISS_WITH_CACHE(LOAD_GLOBAL)
+MISS_WITH_CACHE(LOAD_METHOD)
MISS_WITH_OPARG_COUNTER(BINARY_SUBSCR)
binary_subscr_dict_error: