From fd39f88b5893ca4d355f28f7800b6a6349870f9b Mon Sep 17 00:00:00 2001 From: Mark Shannon Date: Wed, 9 Feb 2022 14:49:26 +0000 Subject: [PATCH 1/2] Reduce number of memory writes to update call_shape.kwnames. --- Python/ceval.c | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/Python/ceval.c b/Python/ceval.c index 02e4e7b9e4d54f..aec9240e4a345f 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -4446,7 +4446,7 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, InterpreterFrame *frame, int thr call_shape.postcall_shrink = 1; call_shape.total_args = oparg; - call_shape.kwnames = NULL; + assert(call_shape.kwnames == NULL); DISPATCH(); } @@ -4485,12 +4485,11 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, InterpreterFrame *frame, int thr call_shape.postcall_shrink = 2-is_method; call_shape.total_args = nargs; - call_shape.kwnames = NULL; + assert(call_shape.kwnames == NULL); DISPATCH(); } TARGET(KW_NAMES) { - assert(call_shape.kwnames == NULL); assert(oparg < PyTuple_GET_SIZE(consts)); call_shape.kwnames = GETITEM(consts, oparg); DISPATCH(); @@ -4526,6 +4525,7 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, InterpreterFrame *frame, int thr tstate, (PyFunctionObject *)function, locals, stack_pointer, positional_args, call_shape.kwnames ); + call_shape.kwnames = NULL; STACK_SHRINK(call_shape.postcall_shrink); // The frame has stolen all the arguments from the stack, // so there is no need to clean them up. @@ -4551,6 +4551,7 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, InterpreterFrame *frame, int thr positional_args | PY_VECTORCALL_ARGUMENTS_OFFSET, call_shape.kwnames); } + call_shape.kwnames = NULL; assert((res != NULL) ^ (_PyErr_Occurred(tstate) != NULL)); Py_DECREF(function); /* Clear the stack */ @@ -4592,6 +4593,7 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, InterpreterFrame *frame, int thr } TARGET(CALL_PY_EXACT_ARGS) { + assert(call_shape.kwnames == NULL); SpecializedCacheEntry *caches = GET_CACHE(); int argcount = call_shape.total_args; DEOPT_IF(!PyFunction_Check(call_shape.callable), CALL); @@ -4620,6 +4622,7 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, InterpreterFrame *frame, int thr } TARGET(CALL_PY_WITH_DEFAULTS) { + assert(call_shape.kwnames == NULL); SpecializedCacheEntry *caches = GET_CACHE(); int argcount = call_shape.total_args; DEOPT_IF(!PyFunction_Check(call_shape.callable), CALL); @@ -4656,9 +4659,9 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, InterpreterFrame *frame, int thr } TARGET(CALL_NO_KW_TYPE_1) { + assert(call_shape.kwnames == NULL); assert(cframe.use_tracing == 0); DEOPT_IF(call_shape.total_args != 1, CALL); - assert(call_shape.kwnames == NULL); PyObject *obj = TOP(); PyObject *callable = SECOND(); DEOPT_IF(callable != (PyObject *)&PyType_Type, CALL); @@ -4671,13 +4674,13 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, InterpreterFrame *frame, int thr } TARGET(CALL_NO_KW_STR_1) { + assert(call_shape.kwnames == NULL); assert(cframe.use_tracing == 0); DEOPT_IF(!PyType_Check(call_shape.callable), CALL); PyTypeObject *tp = (PyTypeObject *)call_shape.callable; DEOPT_IF(call_shape.total_args != 1, CALL); DEOPT_IF(tp != &PyUnicode_Type, CALL); STAT_INC(CALL, hit); - assert(call_shape.kwnames == NULL); PyObject *arg = TOP(); PyObject *res = PyObject_Str(arg); Py_DECREF(arg); @@ -4691,12 +4694,12 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, InterpreterFrame *frame, int thr } TARGET(CALL_NO_KW_TUPLE_1) { + assert(call_shape.kwnames == NULL); DEOPT_IF(!PyType_Check(call_shape.callable), CALL); PyTypeObject *tp = (PyTypeObject *)call_shape.callable; DEOPT_IF(call_shape.total_args != 1, CALL); DEOPT_IF(tp != &PyTuple_Type, CALL); STAT_INC(CALL, hit); - assert(call_shape.kwnames == NULL); PyObject *arg = TOP(); PyObject *res = PySequence_Tuple(arg); Py_DECREF(arg); @@ -4719,6 +4722,7 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, InterpreterFrame *frame, int thr int nargs = call_shape.total_args - kwnames_len; STACK_SHRINK(call_shape.total_args); PyObject *res = tp->tp_vectorcall((PyObject *)tp, stack_pointer, nargs, call_shape.kwnames); + call_shape.kwnames = NULL; /* Free the arguments. */ for (int i = 0; i < call_shape.total_args; i++) { Py_DECREF(stack_pointer[i]); @@ -4828,6 +4832,7 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, InterpreterFrame *frame, int thr call_shape.kwnames ); assert((res != NULL) ^ (_PyErr_Occurred(tstate) != NULL)); + call_shape.kwnames = NULL; /* Free the arguments. */ for (int i = 0; i < call_shape.total_args; i++) { @@ -5393,6 +5398,7 @@ MISS_WITH_OPARG_COUNTER(STORE_SUBSCR) } error: + call_shape.kwnames = NULL; /* Double-check exception status. */ #ifdef NDEBUG if (!_PyErr_Occurred(tstate)) { From dee48561f6b262788a25a29735a3d129bef78603 Mon Sep 17 00:00:00 2001 From: Mark Shannon Date: Wed, 9 Feb 2022 16:51:59 +0000 Subject: [PATCH 2/2] Add comment about call_shape. --- Python/ceval.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Python/ceval.c b/Python/ceval.c index aec9240e4a345f..d98a33bc9d390d 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -1615,7 +1615,9 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, InterpreterFrame *frame, int thr CFrame cframe; CallShape call_shape; - call_shape.kwnames = NULL; // Borrowed reference + call_shape.kwnames = NULL; // Borrowed reference. Reset by CALL instructions. + /* The following three values are always set by the PRECALL instructions. + They are set here to keep the compiler happy. */ call_shape.postcall_shrink = 0; call_shape.total_args = 0; call_shape.callable = NULL; // Strong reference