From 3b12d1b9ccef8a2942dd37a92399ac55730aefd1 Mon Sep 17 00:00:00 2001 From: Fidget-Spinner <28750310+Fidget-Spinner@users.noreply.github.com> Date: Sat, 29 Jan 2022 00:45:38 +0800 Subject: [PATCH 1/9] Near zero-cost super().meth() calls --- Include/internal/pycore_code.h | 8 +- Include/internal/pycore_typeobject.h | 5 ++ Include/opcode.h | 2 + Lib/opcode.py | 3 + .../2022-01-29-00-44-09.bpo-46564.MwLSHf.rst | 5 ++ Objects/typeobject.c | 81 +++++++++++------ Python/ceval.c | 87 ++++++++++++++++++- Python/opcode_targets.h | 4 +- Python/specialize.c | 55 +++++++++++- 9 files changed, 216 insertions(+), 34 deletions(-) create mode 100644 Misc/NEWS.d/next/Core and Builtins/2022-01-29-00-44-09.bpo-46564.MwLSHf.rst diff --git a/Include/internal/pycore_code.h b/Include/internal/pycore_code.h index bbf7a06189ba10..ca7a878d215504 100644 --- a/Include/internal/pycore_code.h +++ b/Include/internal/pycore_code.h @@ -263,6 +263,9 @@ cache_backoff(_PyAdaptiveEntry *entry) { entry->counter = ADAPTIVE_CACHE_BACKOFF; } +/* _interpreter_frame is defined in pycore_frame.h */ +typedef struct _interpreter_frame InterpreterFrame; + /* Specialization functions */ int _Py_Specialize_LoadAttr(PyObject *owner, _Py_CODEUNIT *instr, PyObject *name, SpecializedCacheEntry *cache); @@ -271,7 +274,8 @@ int _Py_Specialize_LoadGlobal(PyObject *globals, PyObject *builtins, _Py_CODEUNI int _Py_Specialize_LoadMethod(PyObject *owner, _Py_CODEUNIT *instr, PyObject *name, SpecializedCacheEntry *cache); int _Py_Specialize_BinarySubscr(PyObject *sub, PyObject *container, _Py_CODEUNIT *instr, SpecializedCacheEntry *cache); int _Py_Specialize_StoreSubscr(PyObject *container, PyObject *sub, _Py_CODEUNIT *instr); -int _Py_Specialize_CallNoKw(PyObject *callable, _Py_CODEUNIT *instr, int nargs, SpecializedCacheEntry *cache, PyObject *builtins); +int _Py_Specialize_CallNoKw(PyObject *callable, _Py_CODEUNIT *instr, int nargs, SpecializedCacheEntry *cache, PyObject *builtins, PyObject **stack_pointer, + InterpreterFrame *frame, PyObject *names); void _Py_Specialize_BinaryOp(PyObject *lhs, PyObject *rhs, _Py_CODEUNIT *instr, SpecializedCacheEntry *cache); void _Py_Specialize_CompareOp(PyObject *lhs, PyObject *rhs, _Py_CODEUNIT *instr, SpecializedCacheEntry *cache); @@ -279,6 +283,8 @@ void _Py_Specialize_CompareOp(PyObject *lhs, PyObject *rhs, _Py_CODEUNIT *instr, /* Deallocator function for static codeobjects used in deepfreeze.py */ void _PyStaticCode_Dealloc(PyCodeObject *co); +#define Py_STATS + #ifdef Py_STATS #define SPECIALIZATION_FAILURE_KINDS 30 diff --git a/Include/internal/pycore_typeobject.h b/Include/internal/pycore_typeobject.h index c480a3a57b436c..9b10398345768e 100644 --- a/Include/internal/pycore_typeobject.h +++ b/Include/internal/pycore_typeobject.h @@ -43,6 +43,11 @@ extern PyStatus _PyTypes_InitSlotDefs(void); extern void _PyStaticType_Dealloc(PyTypeObject *type); +/* _interpreter_frame is defined in pycore_frame.h */ +typedef struct _interpreter_frame InterpreterFrame; + +PyObject *_PySuper_Lookup(PyTypeObject *, PyObject *, PyObject *, int *); +int _PySuper_GetTypeArgs(InterpreterFrame *, PyCodeObject *, PyTypeObject **, PyObject **); #ifdef __cplusplus } diff --git a/Include/opcode.h b/Include/opcode.h index 02cdf42fe2443f..ea32d7e38b333f 100644 --- a/Include/opcode.h +++ b/Include/opcode.h @@ -167,6 +167,8 @@ extern "C" { #define LOAD_FAST__LOAD_CONST 80 #define LOAD_CONST__LOAD_FAST 81 #define STORE_FAST__STORE_FAST 131 +#define CALL_NO_KW_SUPER_0__LOAD_METHOD_CACHED 140 +#define CALL_NO_KW_SUPER_2__LOAD_METHOD_CACHED 141 #define DO_TRACING 255 #ifdef NEED_OPCODE_JUMP_TABLES static uint32_t _PyOpcode_RelativeJump[8] = { diff --git a/Lib/opcode.py b/Lib/opcode.py index 37da05d76d734a..d0fc8b94565575 100644 --- a/Lib/opcode.py +++ b/Lib/opcode.py @@ -280,6 +280,9 @@ def jabs_op(name, op): "LOAD_FAST__LOAD_CONST", "LOAD_CONST__LOAD_FAST", "STORE_FAST__STORE_FAST", + # Specialized super instructions. + "CALL_NO_KW_SUPER_0__LOAD_METHOD_CACHED", + "CALL_NO_KW_SUPER_2__LOAD_METHOD_CACHED", ] _specialization_stats = [ "success", diff --git a/Misc/NEWS.d/next/Core and Builtins/2022-01-29-00-44-09.bpo-46564.MwLSHf.rst b/Misc/NEWS.d/next/Core and Builtins/2022-01-29-00-44-09.bpo-46564.MwLSHf.rst new file mode 100644 index 00000000000000..3f2722c42cbfa6 --- /dev/null +++ b/Misc/NEWS.d/next/Core and Builtins/2022-01-29-00-44-09.bpo-46564.MwLSHf.rst @@ -0,0 +1,5 @@ +Method calls on :class:`super` are sped up. The 2-argument form, +``super(type, obj).meth()`` is now nearly as fast as an equivalent +``self.meth()`` call. The 0-argument form, while still slower, is still +faster than in previous versions of CPython. Patch by Ken Jin, with +additional contributions by Vladimir Matveev. diff --git a/Objects/typeobject.c b/Objects/typeobject.c index 621ad9745d8448..cbc1b06adc3bca 100644 --- a/Objects/typeobject.c +++ b/Objects/typeobject.c @@ -8846,16 +8846,18 @@ super_repr(PyObject *self) ", NULL>", su->type ? su->type->tp_name : "NULL"); } +/* Forward */ +static PyTypeObject *supercheck(PyTypeObject *type, PyObject *obj); static PyObject * -super_getattro(PyObject *self, PyObject *name) +do_super_lookup(superobject *su, PyTypeObject *su_type, PyObject *su_obj, + PyTypeObject *su_obj_type, PyObject *name, int *meth_found) { - superobject *su = (superobject *)self; PyTypeObject *starttype; PyObject *mro; Py_ssize_t i, n; - starttype = su->obj_type; + starttype = su_obj_type; if (starttype == NULL) goto skip; @@ -8875,7 +8877,7 @@ super_getattro(PyObject *self, PyObject *name) /* No need to check the last one: it's gonna be skipped anyway. */ for (i = 0; i+1 < n; i++) { - if ((PyObject *)(su->type) == PyTuple_GET_ITEM(mro, i)) + if ((PyObject *)(su_type) == PyTuple_GET_ITEM(mro, i)) break; } i++; /* skip su->type (if any) */ @@ -8893,17 +8895,25 @@ super_getattro(PyObject *self, PyObject *name) PyObject *res = PyDict_GetItemWithError(dict, name); if (res != NULL) { Py_INCREF(res); - - descrgetfunc f = Py_TYPE(res)->tp_descr_get; - if (f != NULL) { - PyObject *res2; - res2 = f(res, - /* Only pass 'obj' param if this is instance-mode super - (See SF ID #743627) */ - (su->obj == (PyObject *)starttype) ? NULL : su->obj, - (PyObject *)starttype); - Py_DECREF(res); - res = res2; + if (meth_found && + _PyType_HasFeature(Py_TYPE(res), Py_TPFLAGS_METHOD_DESCRIPTOR)) { + *meth_found = 1; + } + else { + if (meth_found) { + *meth_found = 0; + } + descrgetfunc f = Py_TYPE(res)->tp_descr_get; + if (f != NULL) { + PyObject *res2; + res2 = f(res, + /* Only pass 'obj' param if this is instance-mode super + (See SF ID #743627) */ + (su_obj == (PyObject *)starttype) ? NULL : su_obj, + (PyObject *)starttype); + Py_DECREF(res); + res = res2; + } } Py_DECREF(mro); @@ -8919,7 +8929,27 @@ super_getattro(PyObject *self, PyObject *name) Py_DECREF(mro); skip: - return PyObject_GenericGetAttr(self, name); + assert(su != NULL); + return PyObject_GenericGetAttr((PyObject *)su, name); +} + +static PyObject * +super_getattro(PyObject *self, PyObject *name) +{ + superobject *su = (superobject *)self; + return do_super_lookup(su, su->type, su->obj, su->obj_type, name, NULL); +} + +PyObject * +_PySuper_Lookup(PyTypeObject *su_type, PyObject *su_obj, PyObject *name, int *meth_found) +{ + PyTypeObject *starttype = supercheck(su_type, su_obj); + if (starttype == NULL) { + return NULL; + } + PyObject *res = do_super_lookup(NULL, su_type, su_obj, starttype, name, meth_found); + Py_DECREF(starttype); + return res; } static PyTypeObject * @@ -9011,8 +9041,8 @@ super_descr_get(PyObject *self, PyObject *obj, PyObject *type) } } -static int -super_init_without_args(PyFrameObject *f, PyCodeObject *co, +int +_PySuper_GetTypeArgs(InterpreterFrame *f, PyCodeObject *co, PyTypeObject **type_p, PyObject **obj_p) { if (co->co_argcount == 0) { @@ -9021,13 +9051,13 @@ super_init_without_args(PyFrameObject *f, PyCodeObject *co, return -1; } - assert(f->f_frame->f_code->co_nlocalsplus > 0); - PyObject *firstarg = _PyFrame_GetLocalsArray(f->f_frame)[0]; + assert(f->f_code->co_nlocalsplus > 0); + PyObject *firstarg = _PyFrame_GetLocalsArray(f)[0]; // The first argument might be a cell. if (firstarg != NULL && (_PyLocals_GetKind(co->co_localspluskinds, 0) & CO_FAST_CELL)) { // "firstarg" is a cell here unless (very unlikely) super() // was called from the C-API before the first MAKE_CELL op. - if (f->f_frame->f_lasti >= 0) { + if (f->f_lasti >= 0) { assert(_Py_OPCODE(*co->co_firstinstr) == MAKE_CELL || _Py_OPCODE(*co->co_firstinstr) == COPY_FREE_VARS); assert(PyCell_Check(firstarg)); firstarg = PyCell_GET(firstarg); @@ -9047,7 +9077,7 @@ super_init_without_args(PyFrameObject *f, PyCodeObject *co, PyObject *name = PyTuple_GET_ITEM(co->co_localsplusnames, i); assert(PyUnicode_Check(name)); if (_PyUnicode_EqualToASCIIId(name, &PyId___class__)) { - PyObject *cell = _PyFrame_GetLocalsArray(f->f_frame)[i]; + PyObject *cell = _PyFrame_GetLocalsArray(f)[i]; if (cell == NULL || !PyCell_Check(cell)) { PyErr_SetString(PyExc_RuntimeError, "super(): bad __class__ cell"); @@ -9096,17 +9126,14 @@ super_init(PyObject *self, PyObject *args, PyObject *kwds) /* Call super(), without args -- fill in from __class__ and first local variable on the stack. */ PyThreadState *tstate = _PyThreadState_GET(); - PyFrameObject *frame = PyThreadState_GetFrame(tstate); + InterpreterFrame *frame = tstate->cframe->current_frame; if (frame == NULL) { PyErr_SetString(PyExc_RuntimeError, "super(): no current frame"); return -1; } - PyCodeObject *code = PyFrame_GetCode(frame); - int res = super_init_without_args(frame, code, &type, &obj); - Py_DECREF(frame); - Py_DECREF(code); + int res = _PySuper_GetTypeArgs(frame, frame->f_code, &type, &obj); if (res < 0) { return -1; diff --git a/Python/ceval.c b/Python/ceval.c index 106e4080840f44..3fddde7e2f66d1 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -4613,7 +4613,8 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, InterpreterFrame *frame, int thr next_instr--; int nargs = oparg+extra_args; if (_Py_Specialize_CallNoKw( - PEEK(nargs + 1), next_instr, nargs, cache, BUILTINS()) < 0) { + PEEK(nargs + 1), next_instr, nargs, cache, BUILTINS(), + stack_pointer, frame, names) < 0) { goto error; } DISPATCH(); @@ -4930,6 +4931,90 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, InterpreterFrame *frame, int thr DISPATCH(); } + TARGET(CALL_NO_KW_SUPER_0__LOAD_METHOD_CACHED) { + /* super().meth */ + assert(_Py_OPCODE(next_instr[0]) == LOAD_METHOD_ADAPTIVE); + assert(_Py_OPCODE(next_instr[-2]) != PRECALL_METHOD); + SpecializedCacheEntry *caches = GET_CACHE(); + _PyAdaptiveEntry *cache0 = &caches[0].adaptive; + _PyObjectCache *cache1 = &caches[-1].obj; + _PyAdaptiveEntry *lm_adaptive = &caches[-2].adaptive; + int nargs = cache0->original_oparg; + assert(nargs == 0); + + /* CALL_NO_KW_SUPER */ + PyObject *su_obj; + PyTypeObject *su_type; + PyObject *meth; + PyObject *super_callable = TOP(); + + DEOPT_IF(_PyType_CAST(super_callable) != &PySuper_Type, CALL_NO_KW); + + /* super() - zero argument form */ + if (_PySuper_GetTypeArgs(frame, frame->f_code, &su_type, &su_obj) < 0) { + PyErr_Clear(); + DEOPT_IF(1, CALL_NO_KW); + } + assert(su_obj != NULL); + DEOPT_IF(lm_adaptive->version != Py_TYPE(su_obj)->tp_version_tag, CALL_NO_KW); + DEOPT_IF(cache0->version != su_type->tp_version_tag, CALL_NO_KW); + + STAT_INC(CALL_NO_KW, hit); + + /* LOAD_METHOD_CACHED */ + meth = cache1->obj; + assert(meth != NULL && _PyType_HasFeature(Py_TYPE(meth), Py_TPFLAGS_METHOD_DESCRIPTOR)); + Py_INCREF(meth); + SET_TOP(meth); + Py_INCREF(su_obj); + PUSH(su_obj); + + Py_DECREF(super_callable); + next_instr++; + DISPATCH(); + } + + TARGET(CALL_NO_KW_SUPER_2__LOAD_METHOD_CACHED) { + /* super(type, obj).meth */ + assert(_Py_OPCODE(next_instr[0]) == LOAD_METHOD_ADAPTIVE); + assert(_Py_OPCODE(next_instr[-2]) != PRECALL_METHOD); + SpecializedCacheEntry *caches = GET_CACHE(); + _PyAdaptiveEntry *cache0 = &caches[0].adaptive; + _PyObjectCache *cache1 = &caches[-1].obj; + _PyAdaptiveEntry *lm_adaptive = &caches[-2].adaptive; + int nargs = cache0->original_oparg; + int lm_oparg = cache0->index; + assert(nargs == 2); + + /* CALL_NO_KW_SUPER */ + /* super(type, obj) - two argument form */ + PyObject *su_obj = TOP(); + PyTypeObject *su_type = _PyType_CAST(SECOND()); + PyObject *super_callable = THIRD(); + PyObject *meth; + + DEOPT_IF(_PyType_CAST(super_callable) != &PySuper_Type, CALL_NO_KW); + + assert(su_obj != NULL); + DEOPT_IF(lm_adaptive->version != Py_TYPE(su_obj)->tp_version_tag, CALL_NO_KW); + DEOPT_IF(cache0->version != su_type->tp_version_tag, CALL_NO_KW); + + STAT_INC(CALL_NO_KW, hit); + + (void)(POP()); + /* LOAD_METHOD_CACHED */ + meth = cache1->obj; + assert(meth != NULL && _PyType_HasFeature(Py_TYPE(meth), Py_TPFLAGS_METHOD_DESCRIPTOR)); + Py_INCREF(meth); + SET_SECOND(meth); + SET_TOP(su_obj); + + Py_DECREF(super_callable); + Py_DECREF(su_type); + next_instr++; + DISPATCH(); + } + TARGET(CALL_FUNCTION_EX) { PREDICTED(CALL_FUNCTION_EX); PyObject *func, *callargs, *kwargs = NULL, *result; diff --git a/Python/opcode_targets.h b/Python/opcode_targets.h index c6e6d826aee609..acfc6537f7eed1 100644 --- a/Python/opcode_targets.h +++ b/Python/opcode_targets.h @@ -139,8 +139,8 @@ static void *opcode_targets[256] = { &&TARGET_LOAD_DEREF, &&TARGET_STORE_DEREF, &&TARGET_DELETE_DEREF, - &&_unknown_opcode, - &&_unknown_opcode, + &&TARGET_CALL_NO_KW_SUPER_0__LOAD_METHOD_CACHED, + &&TARGET_CALL_NO_KW_SUPER_2__LOAD_METHOD_CACHED, &&TARGET_CALL_FUNCTION_EX, &&_unknown_opcode, &&TARGET_EXTENDED_ARG, diff --git a/Python/specialize.c b/Python/specialize.c index 44c006245ebfc9..8e32a3b11275a5 100644 --- a/Python/specialize.c +++ b/Python/specialize.c @@ -4,6 +4,7 @@ #include "pycore_long.h" #include "pycore_moduleobject.h" #include "pycore_object.h" +#include "pycore_frame.h" #include "opcode.h" #include "structmember.h" // struct PyMemberDef, T_OFFSET_EX @@ -1337,7 +1338,8 @@ _Py_Specialize_StoreSubscr(PyObject *container, PyObject *sub, _Py_CODEUNIT *ins static int specialize_class_call( PyObject *callable, _Py_CODEUNIT *instr, - int nargs, SpecializedCacheEntry *cache) + int nargs, SpecializedCacheEntry *cache, PyObject **stack_pointer, + InterpreterFrame *frame, PyObject *names) { PyTypeObject *tp = _PyType_CAST(callable); if (_Py_OPCODE(instr[-1]) == PRECALL_METHOD) { @@ -1359,6 +1361,51 @@ specialize_class_call( return 0; } } + + _Py_CODEUNIT next_instr = instr[1]; + /* Adaptive super instruction of CALL_NO_KW andLOAD_METHOD_ADAPTIVE. */ + if (tp == &PySuper_Type && + _Py_OPCODE(next_instr) == LOAD_METHOD_ADAPTIVE && + (nargs == 0 || nargs == 2)) { + /* Use load_method cache entries too. */ + _PyAdaptiveEntry *lm_adaptive = &cache[-cache_requirements[CALL_NO_KW]].adaptive; + _PyObjectCache *cache1 = &cache[-1].obj; + PyObject *su_obj; + PyTypeObject *su_type; + PyObject *meth; + int meth_found; + PyObject *name = PyTuple_GET_ITEM(names, lm_adaptive->original_oparg); + + /* Note (KJ): the following operations must not affect tp_version_tag. */ + /* super() zero arg form. */ + if (nargs == 0) { + if (_PySuper_GetTypeArgs(frame, frame->f_code, &su_type, &su_obj) < 0) { + PyErr_Clear(); + goto fail; + } + } + /* super(su_type, su_obj) two arg form. */ + else if (nargs == 2) { + su_type = _PyType_CAST(stack_pointer[-2]); + su_obj = stack_pointer[-1]; + } + meth = _PySuper_Lookup(su_type, su_obj, name, &meth_found); + if (meth == NULL) { + assert(PyErr_Occurred()); + PyErr_Clear(); + goto fail; + } + if (!meth_found) { + goto fail; + } + cache->adaptive.version = su_type->tp_version_tag; + cache1->obj = meth; + lm_adaptive->version = Py_TYPE(su_obj)->tp_version_tag; + *instr = _Py_MAKECODEUNIT(nargs == 0 ? CALL_NO_KW_SUPER_0__LOAD_METHOD_CACHED + : CALL_NO_KW_SUPER_2__LOAD_METHOD_CACHED, _Py_OPARG(*instr)); + return 0; + } +fail: SPECIALIZATION_FAIL(CALL_NO_KW, SPEC_FAIL_CLASS); return -1; } @@ -1560,7 +1607,8 @@ int _Py_Specialize_CallNoKw( PyObject *callable, _Py_CODEUNIT *instr, int nargs, SpecializedCacheEntry *cache, - PyObject *builtins) + PyObject *builtins, PyObject **stack_pointer, InterpreterFrame *frame, + PyObject *names) { int fail; if (PyCFunction_CheckExact(callable)) { @@ -1570,7 +1618,8 @@ _Py_Specialize_CallNoKw( fail = specialize_py_call((PyFunctionObject *)callable, instr, nargs, cache); } else if (PyType_Check(callable)) { - fail = specialize_class_call(callable, instr, nargs, cache); + fail = specialize_class_call(callable, instr, nargs, cache, stack_pointer, + frame, names); } else if (Py_IS_TYPE(callable, &PyMethodDescr_Type)) { fail = specialize_method_descriptor( From 5b7a98d77f27cbad654195c027bc597b64fdfb05 Mon Sep 17 00:00:00 2001 From: Fidget-Spinner <28750310+Fidget-Spinner@users.noreply.github.com> Date: Sat, 29 Jan 2022 00:47:02 +0800 Subject: [PATCH 2/9] remove py_stats --- Include/internal/pycore_code.h | 1 - 1 file changed, 1 deletion(-) diff --git a/Include/internal/pycore_code.h b/Include/internal/pycore_code.h index ca7a878d215504..682c6b4d868e2e 100644 --- a/Include/internal/pycore_code.h +++ b/Include/internal/pycore_code.h @@ -283,7 +283,6 @@ void _Py_Specialize_CompareOp(PyObject *lhs, PyObject *rhs, _Py_CODEUNIT *instr, /* Deallocator function for static codeobjects used in deepfreeze.py */ void _PyStaticCode_Dealloc(PyCodeObject *co); -#define Py_STATS #ifdef Py_STATS From efb0ae37c4863b0c3bb19f8980424656d0343970 Mon Sep 17 00:00:00 2001 From: Fidget-Spinner <28750310+Fidget-Spinner@users.noreply.github.com> Date: Sat, 29 Jan 2022 01:06:55 +0800 Subject: [PATCH 3/9] Add vladima to co-authors Co-Authored-By: Vladimir Matveev --- Include/internal/pycore_code.h | 1 - 1 file changed, 1 deletion(-) diff --git a/Include/internal/pycore_code.h b/Include/internal/pycore_code.h index 682c6b4d868e2e..59e423a3d6b0ed 100644 --- a/Include/internal/pycore_code.h +++ b/Include/internal/pycore_code.h @@ -283,7 +283,6 @@ void _Py_Specialize_CompareOp(PyObject *lhs, PyObject *rhs, _Py_CODEUNIT *instr, /* Deallocator function for static codeobjects used in deepfreeze.py */ void _PyStaticCode_Dealloc(PyCodeObject *co); - #ifdef Py_STATS #define SPECIALIZATION_FAILURE_KINDS 30 From 9d64819e62a7aa6d5b7882bfb5e5b44c349b9058 Mon Sep 17 00:00:00 2001 From: Fidget-Spinner <28750310+Fidget-Spinner@users.noreply.github.com> Date: Sat, 29 Jan 2022 01:38:12 +0800 Subject: [PATCH 4/9] fix merge errors --- Include/internal/pycore_code.h | 2 +- Include/opcode.h | 77 ++++++++++++++++++---------------- Programs/test_frozenmain.h | 59 +++++++++++++------------- Python/ceval.c | 20 ++++----- Python/opcode_targets.h | 6 +-- Python/specialize.c | 4 +- 6 files changed, 85 insertions(+), 83 deletions(-) diff --git a/Include/internal/pycore_code.h b/Include/internal/pycore_code.h index b77bab7f7546b5..f81829733d94c5 100644 --- a/Include/internal/pycore_code.h +++ b/Include/internal/pycore_code.h @@ -275,7 +275,7 @@ int _Py_Specialize_LoadMethod(PyObject *owner, _Py_CODEUNIT *instr, PyObject *na int _Py_Specialize_BinarySubscr(PyObject *sub, PyObject *container, _Py_CODEUNIT *instr, SpecializedCacheEntry *cache); int _Py_Specialize_StoreSubscr(PyObject *container, PyObject *sub, _Py_CODEUNIT *instr); int _Py_Specialize_CallNoKw(PyObject *callable, _Py_CODEUNIT *instr, int nargs, - SpecializedCacheEntry *cache, PyObject *kwnames, PyObject *builtins, + PyObject *kwnames, SpecializedCacheEntry *cache, PyObject *builtins, PyObject **stack_pointer, InterpreterFrame *frame, PyObject *names); void _Py_Specialize_BinaryOp(PyObject *lhs, PyObject *rhs, _Py_CODEUNIT *instr, SpecializedCacheEntry *cache); diff --git a/Include/opcode.h b/Include/opcode.h index c29d23c780a7ab..646c4baae010ae 100644 --- a/Include/opcode.h +++ b/Include/opcode.h @@ -134,42 +134,47 @@ extern "C" { #define STORE_SUBSCR_ADAPTIVE 26 #define STORE_SUBSCR_LIST_INT 27 #define STORE_SUBSCR_DICT 28 -#define CALL_NO_KW_ADAPTIVE 29 -#define CALL_NO_KW_BUILTIN_O 34 -#define CALL_NO_KW_BUILTIN_FAST 36 -#define CALL_NO_KW_LEN 37 -#define CALL_NO_KW_ISINSTANCE 38 -#define CALL_NO_KW_PY_SIMPLE 39 -#define CALL_NO_KW_LIST_APPEND 40 -#define CALL_NO_KW_METHOD_DESCRIPTOR_O 41 -#define CALL_NO_KW_TYPE_1 42 -#define CALL_NO_KW_BUILTIN_CLASS_1 43 -#define CALL_NO_KW_METHOD_DESCRIPTOR_FAST 44 -#define JUMP_ABSOLUTE_QUICK 45 -#define LOAD_ATTR_ADAPTIVE 46 -#define LOAD_ATTR_INSTANCE_VALUE 47 -#define LOAD_ATTR_WITH_HINT 48 -#define LOAD_ATTR_SLOT 55 -#define LOAD_ATTR_MODULE 56 -#define LOAD_GLOBAL_ADAPTIVE 57 -#define LOAD_GLOBAL_MODULE 58 -#define LOAD_GLOBAL_BUILTIN 59 -#define LOAD_METHOD_ADAPTIVE 62 -#define LOAD_METHOD_CACHED 63 -#define LOAD_METHOD_CLASS 64 -#define LOAD_METHOD_MODULE 65 -#define LOAD_METHOD_NO_DICT 66 -#define STORE_ATTR_ADAPTIVE 67 -#define STORE_ATTR_INSTANCE_VALUE 72 -#define STORE_ATTR_SLOT 76 -#define STORE_ATTR_WITH_HINT 77 -#define LOAD_FAST__LOAD_FAST 78 -#define STORE_FAST__LOAD_FAST 79 -#define LOAD_FAST__LOAD_CONST 80 -#define LOAD_CONST__LOAD_FAST 81 -#define STORE_FAST__STORE_FAST 131 -#define CALL_NO_KW_SUPER_0__LOAD_METHOD_CACHED 140 -#define CALL_NO_KW_SUPER_2__LOAD_METHOD_CACHED 141 +#define CALL_ADAPTIVE 29 +#define CALL_BUILTIN_CLASS 34 +#define CALL_NO_KW_BUILTIN_O 36 +#define CALL_NO_KW_BUILTIN_FAST 37 +#define CALL_BUILTIN_FAST_WITH_KEYWORDS 38 +#define CALL_NO_KW_LEN 39 +#define CALL_NO_KW_ISINSTANCE 40 +#define CALL_PY_EXACT_ARGS 41 +#define CALL_PY_WITH_DEFAULTS 42 +#define CALL_NO_KW_LIST_APPEND 43 +#define CALL_NO_KW_METHOD_DESCRIPTOR_O 44 +#define CALL_NO_KW_METHOD_DESCRIPTOR_NOARGS 45 +#define CALL_NO_KW_STR_1 46 +#define CALL_NO_KW_TUPLE_1 47 +#define CALL_NO_KW_TYPE_1 48 +#define CALL_NO_KW_METHOD_DESCRIPTOR_FAST 55 +#define JUMP_ABSOLUTE_QUICK 56 +#define LOAD_ATTR_ADAPTIVE 57 +#define LOAD_ATTR_INSTANCE_VALUE 58 +#define LOAD_ATTR_WITH_HINT 59 +#define LOAD_ATTR_SLOT 62 +#define LOAD_ATTR_MODULE 63 +#define LOAD_GLOBAL_ADAPTIVE 64 +#define LOAD_GLOBAL_MODULE 65 +#define LOAD_GLOBAL_BUILTIN 66 +#define LOAD_METHOD_ADAPTIVE 67 +#define LOAD_METHOD_CACHED 72 +#define LOAD_METHOD_CLASS 76 +#define LOAD_METHOD_MODULE 77 +#define LOAD_METHOD_NO_DICT 78 +#define STORE_ATTR_ADAPTIVE 79 +#define STORE_ATTR_INSTANCE_VALUE 80 +#define STORE_ATTR_SLOT 81 +#define STORE_ATTR_WITH_HINT 131 +#define LOAD_FAST__LOAD_FAST 140 +#define STORE_FAST__LOAD_FAST 141 +#define LOAD_FAST__LOAD_CONST 143 +#define LOAD_CONST__LOAD_FAST 150 +#define STORE_FAST__STORE_FAST 153 +#define CALL_NO_KW_SUPER_0__LOAD_METHOD_CACHED 154 +#define CALL_NO_KW_SUPER_2__LOAD_METHOD_CACHED 158 #define DO_TRACING 255 #ifdef NEED_OPCODE_JUMP_TABLES static uint32_t _PyOpcode_RelativeJump[8] = { diff --git a/Programs/test_frozenmain.h b/Programs/test_frozenmain.h index 00f36ff9bd70fa..4052fc6c92632f 100644 --- a/Programs/test_frozenmain.h +++ b/Programs/test_frozenmain.h @@ -1,36 +1,35 @@ // Auto-generated by Programs/freeze_test_frozenmain.py unsigned char M_test_frozenmain[] = { 227,0,0,0,0,0,0,0,0,0,0,0,0,7,0,0, - 0,0,0,0,0,115,96,0,0,0,151,0,100,0,100,1, + 0,0,0,0,0,115,88,0,0,0,151,0,100,0,100,1, 108,0,90,0,100,0,100,1,108,1,90,1,101,2,100,2, - 167,1,171,0,1,0,101,2,100,3,101,0,106,3,167,2, - 171,0,1,0,101,1,106,4,167,0,171,0,100,4,25,0, - 90,5,100,5,68,0,93,15,90,6,101,2,100,6,101,6, - 155,0,100,7,101,5,101,6,25,0,155,0,157,4,167,1, - 171,0,1,0,113,30,100,1,83,0,41,8,233,0,0,0, - 0,78,122,18,70,114,111,122,101,110,32,72,101,108,108,111, - 32,87,111,114,108,100,122,8,115,121,115,46,97,114,103,118, - 218,6,99,111,110,102,105,103,41,5,90,12,112,114,111,103, - 114,97,109,95,110,97,109,101,218,10,101,120,101,99,117,116, - 97,98,108,101,90,15,117,115,101,95,101,110,118,105,114,111, - 110,109,101,110,116,90,17,99,111,110,102,105,103,117,114,101, - 95,99,95,115,116,100,105,111,90,14,98,117,102,102,101,114, - 101,100,95,115,116,100,105,111,122,7,99,111,110,102,105,103, - 32,122,2,58,32,41,7,218,3,115,121,115,90,17,95,116, - 101,115,116,105,110,116,101,114,110,97,108,99,97,112,105,218, - 5,112,114,105,110,116,218,4,97,114,103,118,90,11,103,101, - 116,95,99,111,110,102,105,103,115,114,2,0,0,0,218,3, - 107,101,121,169,0,243,0,0,0,0,250,18,116,101,115,116, - 95,102,114,111,122,101,110,109,97,105,110,46,112,121,218,8, - 60,109,111,100,117,108,101,62,114,11,0,0,0,1,0,0, - 0,115,18,0,0,0,2,128,8,3,8,1,10,2,14,1, - 14,1,8,1,28,7,4,249,115,20,0,0,0,2,128,8, - 3,8,1,10,2,14,1,14,1,2,7,4,1,2,249,32, - 7,115,96,0,0,0,0,0,1,11,1,11,1,11,1,11, - 1,25,1,25,1,25,1,25,1,6,7,27,1,28,1,28, - 1,28,1,6,7,17,19,22,19,27,1,28,1,28,1,28, - 10,27,10,39,10,41,10,41,42,50,10,51,1,7,12,2, - 1,42,1,42,5,8,5,10,11,41,21,24,11,41,11,41, - 28,34,35,38,28,39,11,41,11,41,5,42,5,42,5,42, + 169,1,1,0,101,2,100,3,101,0,106,3,169,2,1,0, + 101,1,106,4,169,0,100,4,25,0,90,5,100,5,68,0, + 93,14,90,6,101,2,100,6,101,6,155,0,100,7,101,5, + 101,6,25,0,155,0,157,4,169,1,1,0,113,27,100,1, + 83,0,41,8,233,0,0,0,0,78,122,18,70,114,111,122, + 101,110,32,72,101,108,108,111,32,87,111,114,108,100,122,8, + 115,121,115,46,97,114,103,118,218,6,99,111,110,102,105,103, + 41,5,90,12,112,114,111,103,114,97,109,95,110,97,109,101, + 218,10,101,120,101,99,117,116,97,98,108,101,90,15,117,115, + 101,95,101,110,118,105,114,111,110,109,101,110,116,90,17,99, + 111,110,102,105,103,117,114,101,95,99,95,115,116,100,105,111, + 90,14,98,117,102,102,101,114,101,100,95,115,116,100,105,111, + 122,7,99,111,110,102,105,103,32,122,2,58,32,41,7,218, + 3,115,121,115,90,17,95,116,101,115,116,105,110,116,101,114, + 110,97,108,99,97,112,105,218,5,112,114,105,110,116,218,4, + 97,114,103,118,90,11,103,101,116,95,99,111,110,102,105,103, + 115,114,2,0,0,0,218,3,107,101,121,169,0,243,0,0, + 0,0,250,18,116,101,115,116,95,102,114,111,122,101,110,109, + 97,105,110,46,112,121,218,8,60,109,111,100,117,108,101,62, + 114,11,0,0,0,1,0,0,0,115,18,0,0,0,2,128, + 8,3,8,1,8,2,12,1,12,1,8,1,26,7,4,249, + 115,20,0,0,0,2,128,8,3,8,1,8,2,12,1,12, + 1,2,7,4,1,2,249,30,7,115,88,0,0,0,0,0, + 1,11,1,11,1,11,1,11,1,25,1,25,1,25,1,25, + 1,6,7,27,1,28,1,28,1,6,7,17,19,22,19,27, + 1,28,1,28,10,27,10,39,10,41,42,50,10,51,1,7, + 12,2,1,42,1,42,5,8,5,10,11,41,21,24,11,41, + 11,41,28,34,35,38,28,39,11,41,11,41,5,42,5,42, 5,42,1,42,1,42,114,9,0,0,0, }; diff --git a/Python/ceval.c b/Python/ceval.c index 4110cdbdc76076..bc519e7de80b7c 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -4634,7 +4634,7 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, InterpreterFrame *frame, int thr int err = _Py_Specialize_CallNoKw( call_shape.callable, next_instr, nargs, call_shape.kwnames, cache, BUILTINS(), - tack_pointer, frame, names); + stack_pointer, frame, names); if (err < 0) { goto error; } @@ -5087,18 +5087,18 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, InterpreterFrame *frame, int thr PyObject *meth; PyObject *super_callable = TOP(); - DEOPT_IF(_PyType_CAST(super_callable) != &PySuper_Type, CALL_NO_KW); + DEOPT_IF(_PyType_CAST(super_callable) != &PySuper_Type, CALL); /* super() - zero argument form */ if (_PySuper_GetTypeArgs(frame, frame->f_code, &su_type, &su_obj) < 0) { PyErr_Clear(); - DEOPT_IF(1, CALL_NO_KW); + DEOPT_IF(1, CALL); } assert(su_obj != NULL); - DEOPT_IF(lm_adaptive->version != Py_TYPE(su_obj)->tp_version_tag, CALL_NO_KW); - DEOPT_IF(cache0->version != su_type->tp_version_tag, CALL_NO_KW); + DEOPT_IF(lm_adaptive->version != Py_TYPE(su_obj)->tp_version_tag, CALL); + DEOPT_IF(cache0->version != su_type->tp_version_tag, CALL); - STAT_INC(CALL_NO_KW, hit); + STAT_INC(CALL, hit); /* LOAD_METHOD_CACHED */ meth = cache1->obj; @@ -5132,13 +5132,13 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, InterpreterFrame *frame, int thr PyObject *super_callable = THIRD(); PyObject *meth; - DEOPT_IF(_PyType_CAST(super_callable) != &PySuper_Type, CALL_NO_KW); + DEOPT_IF(_PyType_CAST(super_callable) != &PySuper_Type, CALL); assert(su_obj != NULL); - DEOPT_IF(lm_adaptive->version != Py_TYPE(su_obj)->tp_version_tag, CALL_NO_KW); - DEOPT_IF(cache0->version != su_type->tp_version_tag, CALL_NO_KW); + DEOPT_IF(lm_adaptive->version != Py_TYPE(su_obj)->tp_version_tag, CALL); + DEOPT_IF(cache0->version != su_type->tp_version_tag, CALL); - STAT_INC(CALL_NO_KW, hit); + STAT_INC(CALL, hit); (void)(POP()); /* LOAD_METHOD_CACHED */ diff --git a/Python/opcode_targets.h b/Python/opcode_targets.h index c0076ef0dfa53e..a8a7649280e64c 100644 --- a/Python/opcode_targets.h +++ b/Python/opcode_targets.h @@ -139,8 +139,6 @@ static void *opcode_targets[256] = { &&TARGET_LOAD_DEREF, &&TARGET_STORE_DEREF, &&TARGET_DELETE_DEREF, - &&TARGET_CALL_NO_KW_SUPER_0__LOAD_METHOD_CACHED, - &&TARGET_CALL_NO_KW_SUPER_2__LOAD_METHOD_CACHED, &&TARGET_LOAD_FAST__LOAD_FAST, &&TARGET_STORE_FAST__LOAD_FAST, &&TARGET_CALL_FUNCTION_EX, @@ -155,11 +153,11 @@ static void *opcode_targets[256] = { &&TARGET_RESUME, &&TARGET_MATCH_CLASS, &&TARGET_STORE_FAST__STORE_FAST, - &&_unknown_opcode, + &&TARGET_CALL_NO_KW_SUPER_0__LOAD_METHOD_CACHED, &&TARGET_FORMAT_VALUE, &&TARGET_BUILD_CONST_KEY_MAP, &&TARGET_BUILD_STRING, - &&_unknown_opcode, + &&TARGET_CALL_NO_KW_SUPER_2__LOAD_METHOD_CACHED, &&_unknown_opcode, &&TARGET_LOAD_METHOD, &&_unknown_opcode, diff --git a/Python/specialize.c b/Python/specialize.c index ebc6493b10f438..d67608338e4ce9 100644 --- a/Python/specialize.c +++ b/Python/specialize.c @@ -1385,12 +1385,12 @@ specialize_class_call( } _Py_CODEUNIT next_instr = instr[1]; - /* Adaptive super instruction of CALL_NO_KW andLOAD_METHOD_ADAPTIVE. */ + /* Adaptive super instruction of CALL and LOAD_METHOD_ADAPTIVE. */ if (tp == &PySuper_Type && _Py_OPCODE(next_instr) == LOAD_METHOD_ADAPTIVE && (nargs == 0 || nargs == 2)) { /* Use load_method cache entries too. */ - _PyAdaptiveEntry *lm_adaptive = &cache[-cache_requirements[CALL_NO_KW]].adaptive; + _PyAdaptiveEntry *lm_adaptive = &cache[-cache_requirements[CALL]].adaptive; _PyObjectCache *cache1 = &cache[-1].obj; PyObject *su_obj; PyTypeObject *su_type; From ac19aa1c1955f9c639904a871c9a24e3f769b351 Mon Sep 17 00:00:00 2001 From: Fidget-Spinner <28750310+Fidget-Spinner@users.noreply.github.com> Date: Sat, 29 Jan 2022 01:46:57 +0800 Subject: [PATCH 5/9] Update test_frozenmain.h --- Programs/test_frozenmain.h | 59 +++++++++++++++++++------------------- 1 file changed, 30 insertions(+), 29 deletions(-) diff --git a/Programs/test_frozenmain.h b/Programs/test_frozenmain.h index 4052fc6c92632f..00f36ff9bd70fa 100644 --- a/Programs/test_frozenmain.h +++ b/Programs/test_frozenmain.h @@ -1,35 +1,36 @@ // Auto-generated by Programs/freeze_test_frozenmain.py unsigned char M_test_frozenmain[] = { 227,0,0,0,0,0,0,0,0,0,0,0,0,7,0,0, - 0,0,0,0,0,115,88,0,0,0,151,0,100,0,100,1, + 0,0,0,0,0,115,96,0,0,0,151,0,100,0,100,1, 108,0,90,0,100,0,100,1,108,1,90,1,101,2,100,2, - 169,1,1,0,101,2,100,3,101,0,106,3,169,2,1,0, - 101,1,106,4,169,0,100,4,25,0,90,5,100,5,68,0, - 93,14,90,6,101,2,100,6,101,6,155,0,100,7,101,5, - 101,6,25,0,155,0,157,4,169,1,1,0,113,27,100,1, - 83,0,41,8,233,0,0,0,0,78,122,18,70,114,111,122, - 101,110,32,72,101,108,108,111,32,87,111,114,108,100,122,8, - 115,121,115,46,97,114,103,118,218,6,99,111,110,102,105,103, - 41,5,90,12,112,114,111,103,114,97,109,95,110,97,109,101, - 218,10,101,120,101,99,117,116,97,98,108,101,90,15,117,115, - 101,95,101,110,118,105,114,111,110,109,101,110,116,90,17,99, - 111,110,102,105,103,117,114,101,95,99,95,115,116,100,105,111, - 90,14,98,117,102,102,101,114,101,100,95,115,116,100,105,111, - 122,7,99,111,110,102,105,103,32,122,2,58,32,41,7,218, - 3,115,121,115,90,17,95,116,101,115,116,105,110,116,101,114, - 110,97,108,99,97,112,105,218,5,112,114,105,110,116,218,4, - 97,114,103,118,90,11,103,101,116,95,99,111,110,102,105,103, - 115,114,2,0,0,0,218,3,107,101,121,169,0,243,0,0, - 0,0,250,18,116,101,115,116,95,102,114,111,122,101,110,109, - 97,105,110,46,112,121,218,8,60,109,111,100,117,108,101,62, - 114,11,0,0,0,1,0,0,0,115,18,0,0,0,2,128, - 8,3,8,1,8,2,12,1,12,1,8,1,26,7,4,249, - 115,20,0,0,0,2,128,8,3,8,1,8,2,12,1,12, - 1,2,7,4,1,2,249,30,7,115,88,0,0,0,0,0, - 1,11,1,11,1,11,1,11,1,25,1,25,1,25,1,25, - 1,6,7,27,1,28,1,28,1,6,7,17,19,22,19,27, - 1,28,1,28,10,27,10,39,10,41,42,50,10,51,1,7, - 12,2,1,42,1,42,5,8,5,10,11,41,21,24,11,41, - 11,41,28,34,35,38,28,39,11,41,11,41,5,42,5,42, + 167,1,171,0,1,0,101,2,100,3,101,0,106,3,167,2, + 171,0,1,0,101,1,106,4,167,0,171,0,100,4,25,0, + 90,5,100,5,68,0,93,15,90,6,101,2,100,6,101,6, + 155,0,100,7,101,5,101,6,25,0,155,0,157,4,167,1, + 171,0,1,0,113,30,100,1,83,0,41,8,233,0,0,0, + 0,78,122,18,70,114,111,122,101,110,32,72,101,108,108,111, + 32,87,111,114,108,100,122,8,115,121,115,46,97,114,103,118, + 218,6,99,111,110,102,105,103,41,5,90,12,112,114,111,103, + 114,97,109,95,110,97,109,101,218,10,101,120,101,99,117,116, + 97,98,108,101,90,15,117,115,101,95,101,110,118,105,114,111, + 110,109,101,110,116,90,17,99,111,110,102,105,103,117,114,101, + 95,99,95,115,116,100,105,111,90,14,98,117,102,102,101,114, + 101,100,95,115,116,100,105,111,122,7,99,111,110,102,105,103, + 32,122,2,58,32,41,7,218,3,115,121,115,90,17,95,116, + 101,115,116,105,110,116,101,114,110,97,108,99,97,112,105,218, + 5,112,114,105,110,116,218,4,97,114,103,118,90,11,103,101, + 116,95,99,111,110,102,105,103,115,114,2,0,0,0,218,3, + 107,101,121,169,0,243,0,0,0,0,250,18,116,101,115,116, + 95,102,114,111,122,101,110,109,97,105,110,46,112,121,218,8, + 60,109,111,100,117,108,101,62,114,11,0,0,0,1,0,0, + 0,115,18,0,0,0,2,128,8,3,8,1,10,2,14,1, + 14,1,8,1,28,7,4,249,115,20,0,0,0,2,128,8, + 3,8,1,10,2,14,1,14,1,2,7,4,1,2,249,32, + 7,115,96,0,0,0,0,0,1,11,1,11,1,11,1,11, + 1,25,1,25,1,25,1,25,1,6,7,27,1,28,1,28, + 1,28,1,6,7,17,19,22,19,27,1,28,1,28,1,28, + 10,27,10,39,10,41,10,41,42,50,10,51,1,7,12,2, + 1,42,1,42,5,8,5,10,11,41,21,24,11,41,11,41, + 28,34,35,38,28,39,11,41,11,41,5,42,5,42,5,42, 5,42,1,42,1,42,114,9,0,0,0, }; From 1f9bb6cfb98be80c2841431557ad139413b2a0ac Mon Sep 17 00:00:00 2001 From: Fidget-Spinner <28750310+Fidget-Spinner@users.noreply.github.com> Date: Sat, 29 Jan 2022 02:07:43 +0800 Subject: [PATCH 6/9] fix formatting, correct specialization --- Python/ceval.c | 4 -- Python/specialize.c | 89 +++++++++++++++++++++++---------------------- 2 files changed, 46 insertions(+), 47 deletions(-) diff --git a/Python/ceval.c b/Python/ceval.c index bc519e7de80b7c..2067f06fa4e4d7 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -5088,7 +5088,6 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, InterpreterFrame *frame, int thr PyObject *super_callable = TOP(); DEOPT_IF(_PyType_CAST(super_callable) != &PySuper_Type, CALL); - /* super() - zero argument form */ if (_PySuper_GetTypeArgs(frame, frame->f_code, &su_type, &su_obj) < 0) { PyErr_Clear(); @@ -5097,7 +5096,6 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, InterpreterFrame *frame, int thr assert(su_obj != NULL); DEOPT_IF(lm_adaptive->version != Py_TYPE(su_obj)->tp_version_tag, CALL); DEOPT_IF(cache0->version != su_type->tp_version_tag, CALL); - STAT_INC(CALL, hit); /* LOAD_METHOD_CACHED */ @@ -5133,11 +5131,9 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, InterpreterFrame *frame, int thr PyObject *meth; DEOPT_IF(_PyType_CAST(super_callable) != &PySuper_Type, CALL); - assert(su_obj != NULL); DEOPT_IF(lm_adaptive->version != Py_TYPE(su_obj)->tp_version_tag, CALL); DEOPT_IF(cache0->version != su_type->tp_version_tag, CALL); - STAT_INC(CALL, hit); (void)(POP()); diff --git a/Python/specialize.c b/Python/specialize.c index d67608338e4ce9..c655ddce2a9170 100644 --- a/Python/specialize.c +++ b/Python/specialize.c @@ -1375,6 +1375,52 @@ specialize_class_call( return 0; } } + /* Adaptive super instruction of CALL and LOAD_METHOD_ADAPTIVE. */ + if (tp == &PySuper_Type && + _Py_OPCODE(instr[1]) == LOAD_METHOD_ADAPTIVE && + _Py_OPCODE(instr[-1]) == PRECALL_FUNCTION && + (nargs == 0 || nargs == 2)) { + /* Use load_method cache entries too. */ + _PyAdaptiveEntry *lm_adaptive = &cache[-cache_requirements[CALL]].adaptive; + _PyObjectCache *cache1 = &cache[-1].obj; + PyObject *su_obj; + PyTypeObject *su_type; + PyObject *meth; + int meth_found; + PyObject *name = PyTuple_GET_ITEM(names, lm_adaptive->original_oparg); + + /* Note (KJ): the following operations must not affect tp_version_tag. */ + /* super() zero arg form. */ + if (nargs == 0) { + if (_PySuper_GetTypeArgs(frame, frame->f_code, &su_type, &su_obj) < 0) { + PyErr_Clear(); + SPECIALIZATION_FAIL(CALL, SPEC_FAIL_NOT_DESCRIPTOR); + return -1; + } + } + /* super(su_type, su_obj) two arg form. */ + else if (nargs == 2) { + su_type = _PyType_CAST(stack_pointer[-2]); + su_obj = stack_pointer[-1]; + } + meth = _PySuper_Lookup(su_type, su_obj, name, &meth_found); + if (meth == NULL) { + assert(PyErr_Occurred()); + PyErr_Clear(); + SPECIALIZATION_FAIL(CALL, SPEC_FAIL_OTHER); + return -1; + } + if (!meth_found) { + SPECIALIZATION_FAIL(CALL, SPEC_FAIL_NOT_DESCRIPTOR); + return -1; + } + cache->adaptive.version = su_type->tp_version_tag; + cache1->obj = meth; + lm_adaptive->version = Py_TYPE(su_obj)->tp_version_tag; + *instr = _Py_MAKECODEUNIT(nargs == 0 ? CALL_NO_KW_SUPER_0__LOAD_METHOD_CACHED + : CALL_NO_KW_SUPER_2__LOAD_METHOD_CACHED, _Py_OPARG(*instr)); + return 0; + } if (tp->tp_vectorcall != NULL) { *instr = _Py_MAKECODEUNIT(CALL_BUILTIN_CLASS, _Py_OPARG(*instr)); return 0; @@ -1384,49 +1430,6 @@ specialize_class_call( return -1; } - _Py_CODEUNIT next_instr = instr[1]; - /* Adaptive super instruction of CALL and LOAD_METHOD_ADAPTIVE. */ - if (tp == &PySuper_Type && - _Py_OPCODE(next_instr) == LOAD_METHOD_ADAPTIVE && - (nargs == 0 || nargs == 2)) { - /* Use load_method cache entries too. */ - _PyAdaptiveEntry *lm_adaptive = &cache[-cache_requirements[CALL]].adaptive; - _PyObjectCache *cache1 = &cache[-1].obj; - PyObject *su_obj; - PyTypeObject *su_type; - PyObject *meth; - int meth_found; - PyObject *name = PyTuple_GET_ITEM(names, lm_adaptive->original_oparg); - - /* Note (KJ): the following operations must not affect tp_version_tag. */ - /* super() zero arg form. */ - if (nargs == 0) { - if (_PySuper_GetTypeArgs(frame, frame->f_code, &su_type, &su_obj) < 0) { - PyErr_Clear(); - goto fail; - } - } - /* super(su_type, su_obj) two arg form. */ - else if (nargs == 2) { - su_type = _PyType_CAST(stack_pointer[-2]); - su_obj = stack_pointer[-1]; - } - meth = _PySuper_Lookup(su_type, su_obj, name, &meth_found); - if (meth == NULL) { - assert(PyErr_Occurred()); - PyErr_Clear(); - goto fail; - } - if (!meth_found) { - goto fail; - } - cache->adaptive.version = su_type->tp_version_tag; - cache1->obj = meth; - lm_adaptive->version = Py_TYPE(su_obj)->tp_version_tag; - *instr = _Py_MAKECODEUNIT(nargs == 0 ? CALL_NO_KW_SUPER_0__LOAD_METHOD_CACHED - : CALL_NO_KW_SUPER_2__LOAD_METHOD_CACHED, _Py_OPARG(*instr)); - return 0; - } fail: SPECIALIZATION_FAIL(CALL, SPEC_FAIL_CLASS_MUTABLE); return -1; From f4cd3f9aa3f73b34c2bc3a85431d42b993938e70 Mon Sep 17 00:00:00 2001 From: Fidget-Spinner <28750310+Fidget-Spinner@users.noreply.github.com> Date: Sat, 29 Jan 2022 02:22:52 +0800 Subject: [PATCH 7/9] work with new call convention --- Python/ceval.c | 6 +++--- Python/specialize.c | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Python/ceval.c b/Python/ceval.c index 2067f06fa4e4d7..31843b005ac8a4 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -5078,7 +5078,7 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, InterpreterFrame *frame, int thr _PyAdaptiveEntry *cache0 = &caches[0].adaptive; _PyObjectCache *cache1 = &caches[-1].obj; _PyAdaptiveEntry *lm_adaptive = &caches[-2].adaptive; - int nargs = cache0->original_oparg; + int nargs = call_shape.total_args; assert(nargs == 0); /* CALL_NO_KW_SUPER */ @@ -5119,9 +5119,9 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, InterpreterFrame *frame, int thr _PyAdaptiveEntry *cache0 = &caches[0].adaptive; _PyObjectCache *cache1 = &caches[-1].obj; _PyAdaptiveEntry *lm_adaptive = &caches[-2].adaptive; - int nargs = cache0->original_oparg; - int lm_oparg = cache0->index; + int nargs = call_shape.total_args; assert(nargs == 2); + assert(call_shape.kwnames == NULL); /* CALL_NO_KW_SUPER */ /* super(type, obj) - two argument form */ diff --git a/Python/specialize.c b/Python/specialize.c index c655ddce2a9170..4fe6a5a990f62e 100644 --- a/Python/specialize.c +++ b/Python/specialize.c @@ -1377,6 +1377,7 @@ specialize_class_call( } /* Adaptive super instruction of CALL and LOAD_METHOD_ADAPTIVE. */ if (tp == &PySuper_Type && + kwnames == NULL && _Py_OPCODE(instr[1]) == LOAD_METHOD_ADAPTIVE && _Py_OPCODE(instr[-1]) == PRECALL_FUNCTION && (nargs == 0 || nargs == 2)) { @@ -1430,7 +1431,6 @@ specialize_class_call( return -1; } -fail: SPECIALIZATION_FAIL(CALL, SPEC_FAIL_CLASS_MUTABLE); return -1; } From 19880a919812b51167981fef4e08d6ec64730028 Mon Sep 17 00:00:00 2001 From: Fidget-Spinner <28750310+Fidget-Spinner@users.noreply.github.com> Date: Sat, 29 Jan 2022 21:27:04 +0800 Subject: [PATCH 8/9] partially address review: fix tests, add asserts, add cache modification guards --- Objects/typeobject.c | 6 +++++- Python/ceval.c | 12 ++++++++---- Python/specialize.c | 11 ++++++++++- 3 files changed, 23 insertions(+), 6 deletions(-) diff --git a/Objects/typeobject.c b/Objects/typeobject.c index cbc1b06adc3bca..8bcc90c4474ff7 100644 --- a/Objects/typeobject.c +++ b/Objects/typeobject.c @@ -8929,7 +8929,11 @@ do_super_lookup(superobject *su, PyTypeObject *su_type, PyObject *su_obj, Py_DECREF(mro); skip: - assert(su != NULL); + /* only happens when using manual _PySuper_Lookup, never happens in super_getattro */ + if (su == NULL) { + PyErr_BadInternalCall(); + return NULL; + } return PyObject_GenericGetAttr((PyObject *)su, name); } diff --git a/Python/ceval.c b/Python/ceval.c index 31843b005ac8a4..049f9cda1dc54a 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -1361,6 +1361,7 @@ eval_frame_handle_pending(PyThreadState *tstate) /* The integer overflow is checked by an assertion below. */ #define INSTR_OFFSET() ((int)(next_instr - first_instr)) +#define NEXT_INSTR_OFFSET() ((int)(next_instr+1 - first_instr)) #define NEXTOPARG() do { \ _Py_CODEUNIT word = *next_instr; \ opcode = _Py_OPCODE(word); \ @@ -1486,6 +1487,9 @@ eval_frame_handle_pending(PyThreadState *tstate) #define GET_CACHE() \ _GetSpecializedCacheEntryForInstruction(first_instr, INSTR_OFFSET(), oparg) +# define GET_NEXT_INSTR_CACHE() \ + _GetSpecializedCacheEntryForInstruction(first_instr, NEXT_INSTR_OFFSET(), \ + _Py_OPARG(*next_instr)) #define DEOPT_IF(cond, instname) if (cond) { goto instname ## _miss; } @@ -5078,8 +5082,8 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, InterpreterFrame *frame, int thr _PyAdaptiveEntry *cache0 = &caches[0].adaptive; _PyObjectCache *cache1 = &caches[-1].obj; _PyAdaptiveEntry *lm_adaptive = &caches[-2].adaptive; - int nargs = call_shape.total_args; - assert(nargs == 0); + assert(lm_adaptive == &GET_NEXT_INSTR_CACHE()[0].adaptive); + assert(call_shape.total_args == 0); /* CALL_NO_KW_SUPER */ PyObject *su_obj; @@ -5119,8 +5123,8 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, InterpreterFrame *frame, int thr _PyAdaptiveEntry *cache0 = &caches[0].adaptive; _PyObjectCache *cache1 = &caches[-1].obj; _PyAdaptiveEntry *lm_adaptive = &caches[-2].adaptive; - int nargs = call_shape.total_args; - assert(nargs == 2); + assert(lm_adaptive == &GET_NEXT_INSTR_CACHE()[0].adaptive); + assert(call_shape.total_args == 2); assert(call_shape.kwnames == NULL); /* CALL_NO_KW_SUPER */ diff --git a/Python/specialize.c b/Python/specialize.c index 4fe6a5a990f62e..104921de4cdd80 100644 --- a/Python/specialize.c +++ b/Python/specialize.c @@ -962,6 +962,12 @@ _Py_Specialize_LoadMethod(PyObject *owner, _Py_CODEUNIT *instr, PyObject *name, _PyObjectCache *cache2 = &cache[-2].obj; PyTypeObject *owner_cls = Py_TYPE(owner); + _Py_CODEUNIT prev_instr = _Py_OPCODE(instr[-1]); + if (prev_instr == CALL_NO_KW_SUPER_0__LOAD_METHOD_CACHED || + prev_instr == CALL_NO_KW_SUPER_2__LOAD_METHOD_CACHED) { + /* Our own cache entries are already being used by superinstructions. */ + goto fail; + } if (PyModule_CheckExact(owner)) { int err = specialize_module_load_attr(owner, instr, name, cache0, cache1, LOAD_METHOD, LOAD_METHOD_MODULE); @@ -1378,6 +1384,10 @@ specialize_class_call( /* Adaptive super instruction of CALL and LOAD_METHOD_ADAPTIVE. */ if (tp == &PySuper_Type && kwnames == NULL && + /* Important: this also protects us from accidentally overriding a + the next specialized instruction's cache. We can only use the + subsequent LOAD_METHOD cache if it hasn't specialized yet. + */ _Py_OPCODE(instr[1]) == LOAD_METHOD_ADAPTIVE && _Py_OPCODE(instr[-1]) == PRECALL_FUNCTION && (nargs == 0 || nargs == 2)) { @@ -1430,7 +1440,6 @@ specialize_class_call( SPEC_FAIL_CALL_STR : SPEC_FAIL_CLASS_NO_VECTORCALL); return -1; } - SPECIALIZATION_FAIL(CALL, SPEC_FAIL_CLASS_MUTABLE); return -1; } From 696a0e8c3591ba276c6cc102573010aa228939ed Mon Sep 17 00:00:00 2001 From: Fidget-Spinner <28750310+Fidget-Spinner@users.noreply.github.com> Date: Sat, 29 Jan 2022 21:50:46 +0800 Subject: [PATCH 9/9] fix refleak --- Python/specialize.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Python/specialize.c b/Python/specialize.c index 104921de4cdd80..1986a19df6a0d5 100644 --- a/Python/specialize.c +++ b/Python/specialize.c @@ -1421,12 +1421,13 @@ specialize_class_call( SPECIALIZATION_FAIL(CALL, SPEC_FAIL_OTHER); return -1; } + Py_DECREF(meth); if (!meth_found) { SPECIALIZATION_FAIL(CALL, SPEC_FAIL_NOT_DESCRIPTOR); return -1; } cache->adaptive.version = su_type->tp_version_tag; - cache1->obj = meth; + cache1->obj = meth; /* borrowed */ lm_adaptive->version = Py_TYPE(su_obj)->tp_version_tag; *instr = _Py_MAKECODEUNIT(nargs == 0 ? CALL_NO_KW_SUPER_0__LOAD_METHOD_CACHED : CALL_NO_KW_SUPER_2__LOAD_METHOD_CACHED, _Py_OPARG(*instr));