From b9d070f4be45375c4f07f288df9eb2474e8bc92c Mon Sep 17 00:00:00 2001 From: Guido van Rossum Date: Wed, 12 Jul 2023 19:38:28 -0700 Subject: [PATCH 1/4] Allow using 'kwnames' -- this adds 14 new opcodes Not all of these are useful, but I think they all work. It leaves another 14 CALL specializations that need to be split (or need gh-106603, allowing oparg *and* one cache entry per uop). --- Include/internal/pycore_opcode_metadata.h | 14 + Python/ceval.c | 2 + Python/executor_cases.c.h | 448 ++++++++++++++++++++++ Python/optimizer.c | 14 + Tools/cases_generator/generate_cases.py | 16 +- 5 files changed, 489 insertions(+), 5 deletions(-) diff --git a/Include/internal/pycore_opcode_metadata.h b/Include/internal/pycore_opcode_metadata.h index 8373f56653b1c7..c252bffccce69e 100644 --- a/Include/internal/pycore_opcode_metadata.h +++ b/Include/internal/pycore_opcode_metadata.h @@ -1309,7 +1309,21 @@ const struct opcode_macro_expansion _PyOpcode_macro_expansion[OPCODE_MACRO_EXPAN [GET_YIELD_FROM_ITER] = { .nuops = 1, .uops = { { GET_YIELD_FROM_ITER, 0, 0 } } }, [WITH_EXCEPT_START] = { .nuops = 1, .uops = { { WITH_EXCEPT_START, 0, 0 } } }, [PUSH_EXC_INFO] = { .nuops = 1, .uops = { { PUSH_EXC_INFO, 0, 0 } } }, + [KW_NAMES] = { .nuops = 1, .uops = { { KW_NAMES, 0, 0 } } }, + [CALL_NO_KW_TYPE_1] = { .nuops = 1, .uops = { { CALL_NO_KW_TYPE_1, 0, 0 } } }, + [CALL_NO_KW_STR_1] = { .nuops = 1, .uops = { { CALL_NO_KW_STR_1, 0, 0 } } }, + [CALL_NO_KW_TUPLE_1] = { .nuops = 1, .uops = { { CALL_NO_KW_TUPLE_1, 0, 0 } } }, [EXIT_INIT_CHECK] = { .nuops = 1, .uops = { { EXIT_INIT_CHECK, 0, 0 } } }, + [CALL_BUILTIN_CLASS] = { .nuops = 1, .uops = { { CALL_BUILTIN_CLASS, 0, 0 } } }, + [CALL_NO_KW_BUILTIN_O] = { .nuops = 1, .uops = { { CALL_NO_KW_BUILTIN_O, 0, 0 } } }, + [CALL_NO_KW_BUILTIN_FAST] = { .nuops = 1, .uops = { { CALL_NO_KW_BUILTIN_FAST, 0, 0 } } }, + [CALL_BUILTIN_FAST_WITH_KEYWORDS] = { .nuops = 1, .uops = { { CALL_BUILTIN_FAST_WITH_KEYWORDS, 0, 0 } } }, + [CALL_NO_KW_LEN] = { .nuops = 1, .uops = { { CALL_NO_KW_LEN, 0, 0 } } }, + [CALL_NO_KW_ISINSTANCE] = { .nuops = 1, .uops = { { CALL_NO_KW_ISINSTANCE, 0, 0 } } }, + [CALL_NO_KW_METHOD_DESCRIPTOR_O] = { .nuops = 1, .uops = { { CALL_NO_KW_METHOD_DESCRIPTOR_O, 0, 0 } } }, + [CALL_METHOD_DESCRIPTOR_FAST_WITH_KEYWORDS] = { .nuops = 1, .uops = { { CALL_METHOD_DESCRIPTOR_FAST_WITH_KEYWORDS, 0, 0 } } }, + [CALL_NO_KW_METHOD_DESCRIPTOR_NOARGS] = { .nuops = 1, .uops = { { CALL_NO_KW_METHOD_DESCRIPTOR_NOARGS, 0, 0 } } }, + [CALL_NO_KW_METHOD_DESCRIPTOR_FAST] = { .nuops = 1, .uops = { { CALL_NO_KW_METHOD_DESCRIPTOR_FAST, 0, 0 } } }, [MAKE_FUNCTION] = { .nuops = 1, .uops = { { MAKE_FUNCTION, 0, 0 } } }, [SET_FUNCTION_ATTRIBUTE] = { .nuops = 1, .uops = { { SET_FUNCTION_ATTRIBUTE, 0, 0 } } }, [BUILD_SLICE] = { .nuops = 1, .uops = { { BUILD_SLICE, 0, 0 } } }, diff --git a/Python/ceval.c b/Python/ceval.c index d6c72fa3ff386c..146d7618538e20 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -2746,6 +2746,8 @@ _PyUopExecute(_PyExecutorObject *executor, _PyInterpreterFrame *frame, PyObject int opcode; uint64_t operand; int oparg; + PyObject *kwnames = NULL; // Borrowed reference. Reset by CALL instructions. + for (;;) { opcode = self->trace[pc].opcode; operand = self->trace[pc].operand; diff --git a/Python/executor_cases.c.h b/Python/executor_cases.c.h index ae21ffad94d801..a0b492f025395d 100644 --- a/Python/executor_cases.c.h +++ b/Python/executor_cases.c.h @@ -1904,6 +1904,75 @@ break; } + case KW_NAMES: { + assert(kwnames == NULL); + assert(oparg < PyTuple_GET_SIZE(FRAME_CO_CONSTS)); + kwnames = GETITEM(FRAME_CO_CONSTS, oparg); + break; + } + + case CALL_NO_KW_TYPE_1: { + PyObject **args = (stack_pointer - oparg); + PyObject *callable = stack_pointer[-(1 + oparg)]; + PyObject *null = stack_pointer[-(2 + oparg)]; + PyObject *res; + assert(kwnames == NULL); + assert(oparg == 1); + DEOPT_IF(null != NULL, CALL); + PyObject *obj = args[0]; + DEOPT_IF(callable != (PyObject *)&PyType_Type, CALL); + STAT_INC(CALL, hit); + res = Py_NewRef(Py_TYPE(obj)); + Py_DECREF(obj); + Py_DECREF(&PyType_Type); // I.e., callable + STACK_SHRINK(oparg); + STACK_SHRINK(1); + stack_pointer[-1] = res; + break; + } + + case CALL_NO_KW_STR_1: { + PyObject **args = (stack_pointer - oparg); + PyObject *callable = stack_pointer[-(1 + oparg)]; + PyObject *null = stack_pointer[-(2 + oparg)]; + PyObject *res; + assert(kwnames == NULL); + assert(oparg == 1); + DEOPT_IF(null != NULL, CALL); + DEOPT_IF(callable != (PyObject *)&PyUnicode_Type, CALL); + STAT_INC(CALL, hit); + PyObject *arg = args[0]; + res = PyObject_Str(arg); + Py_DECREF(arg); + Py_DECREF(&PyUnicode_Type); // I.e., callable + if (res == NULL) { STACK_SHRINK(oparg); goto pop_2_error; } + STACK_SHRINK(oparg); + STACK_SHRINK(1); + stack_pointer[-1] = res; + break; + } + + case CALL_NO_KW_TUPLE_1: { + PyObject **args = (stack_pointer - oparg); + PyObject *callable = stack_pointer[-(1 + oparg)]; + PyObject *null = stack_pointer[-(2 + oparg)]; + PyObject *res; + assert(kwnames == NULL); + assert(oparg == 1); + DEOPT_IF(null != NULL, CALL); + DEOPT_IF(callable != (PyObject *)&PyTuple_Type, CALL); + STAT_INC(CALL, hit); + PyObject *arg = args[0]; + res = PySequence_Tuple(arg); + Py_DECREF(arg); + Py_DECREF(&PyTuple_Type); // I.e., tuple + if (res == NULL) { STACK_SHRINK(oparg); goto pop_2_error; } + STACK_SHRINK(oparg); + STACK_SHRINK(1); + stack_pointer[-1] = res; + break; + } + case EXIT_INIT_CHECK: { PyObject *should_be_none = stack_pointer[-1]; assert(STACK_LEVEL() == 2); @@ -1917,6 +1986,385 @@ break; } + case CALL_BUILTIN_CLASS: { + PyObject **args = (stack_pointer - oparg); + PyObject *callable = stack_pointer[-(1 + oparg)]; + PyObject *method = stack_pointer[-(2 + oparg)]; + PyObject *res; + int is_meth = method != NULL; + int total_args = oparg; + if (is_meth) { + callable = method; + args--; + total_args++; + } + int kwnames_len = KWNAMES_LEN(); + DEOPT_IF(!PyType_Check(callable), CALL); + PyTypeObject *tp = (PyTypeObject *)callable; + DEOPT_IF(tp->tp_vectorcall == NULL, CALL); + STAT_INC(CALL, hit); + res = tp->tp_vectorcall((PyObject *)tp, args, + total_args - kwnames_len, kwnames); + kwnames = NULL; + /* Free the arguments. */ + for (int i = 0; i < total_args; i++) { + Py_DECREF(args[i]); + } + Py_DECREF(tp); + if (res == NULL) { STACK_SHRINK(oparg); goto pop_2_error; } + STACK_SHRINK(oparg); + STACK_SHRINK(1); + stack_pointer[-1] = res; + break; + } + + case CALL_NO_KW_BUILTIN_O: { + PyObject **args = (stack_pointer - oparg); + PyObject *callable = stack_pointer[-(1 + oparg)]; + PyObject *method = stack_pointer[-(2 + oparg)]; + PyObject *res; + /* Builtin METH_O functions */ + assert(kwnames == NULL); + int is_meth = method != NULL; + int total_args = oparg; + if (is_meth) { + callable = method; + args--; + total_args++; + } + DEOPT_IF(total_args != 1, CALL); + DEOPT_IF(!PyCFunction_CheckExact(callable), CALL); + DEOPT_IF(PyCFunction_GET_FLAGS(callable) != METH_O, CALL); + STAT_INC(CALL, hit); + PyCFunction cfunc = PyCFunction_GET_FUNCTION(callable); + // This is slower but CPython promises to check all non-vectorcall + // function calls. + if (_Py_EnterRecursiveCallTstate(tstate, " while calling a Python object")) { + goto error; + } + PyObject *arg = args[0]; + res = _PyCFunction_TrampolineCall(cfunc, PyCFunction_GET_SELF(callable), arg); + _Py_LeaveRecursiveCallTstate(tstate); + assert((res != NULL) ^ (_PyErr_Occurred(tstate) != NULL)); + + Py_DECREF(arg); + Py_DECREF(callable); + if (res == NULL) { STACK_SHRINK(oparg); goto pop_2_error; } + STACK_SHRINK(oparg); + STACK_SHRINK(1); + stack_pointer[-1] = res; + break; + } + + case CALL_NO_KW_BUILTIN_FAST: { + PyObject **args = (stack_pointer - oparg); + PyObject *callable = stack_pointer[-(1 + oparg)]; + PyObject *method = stack_pointer[-(2 + oparg)]; + PyObject *res; + /* Builtin METH_FASTCALL functions, without keywords */ + assert(kwnames == NULL); + int is_meth = method != NULL; + int total_args = oparg; + if (is_meth) { + callable = method; + args--; + total_args++; + } + DEOPT_IF(!PyCFunction_CheckExact(callable), CALL); + DEOPT_IF(PyCFunction_GET_FLAGS(callable) != METH_FASTCALL, CALL); + STAT_INC(CALL, hit); + PyCFunction cfunc = PyCFunction_GET_FUNCTION(callable); + /* res = func(self, args, nargs) */ + res = ((_PyCFunctionFast)(void(*)(void))cfunc)( + PyCFunction_GET_SELF(callable), + args, + total_args); + assert((res != NULL) ^ (_PyErr_Occurred(tstate) != NULL)); + + /* Free the arguments. */ + for (int i = 0; i < total_args; i++) { + Py_DECREF(args[i]); + } + Py_DECREF(callable); + if (res == NULL) { STACK_SHRINK(oparg); goto pop_2_error; } + /* Not deopting because this doesn't mean our optimization was + wrong. `res` can be NULL for valid reasons. Eg. getattr(x, + 'invalid'). In those cases an exception is set, so we must + handle it. + */ + STACK_SHRINK(oparg); + STACK_SHRINK(1); + stack_pointer[-1] = res; + break; + } + + case CALL_BUILTIN_FAST_WITH_KEYWORDS: { + PyObject **args = (stack_pointer - oparg); + PyObject *callable = stack_pointer[-(1 + oparg)]; + PyObject *method = stack_pointer[-(2 + oparg)]; + PyObject *res; + /* Builtin METH_FASTCALL | METH_KEYWORDS functions */ + int is_meth = method != NULL; + int total_args = oparg; + if (is_meth) { + callable = method; + args--; + total_args++; + } + DEOPT_IF(!PyCFunction_CheckExact(callable), CALL); + DEOPT_IF(PyCFunction_GET_FLAGS(callable) != + (METH_FASTCALL | METH_KEYWORDS), CALL); + STAT_INC(CALL, hit); + /* res = func(self, args, nargs, kwnames) */ + _PyCFunctionFastWithKeywords cfunc = + (_PyCFunctionFastWithKeywords)(void(*)(void)) + PyCFunction_GET_FUNCTION(callable); + res = cfunc( + PyCFunction_GET_SELF(callable), + args, + total_args - KWNAMES_LEN(), + kwnames + ); + assert((res != NULL) ^ (_PyErr_Occurred(tstate) != NULL)); + kwnames = NULL; + + /* Free the arguments. */ + for (int i = 0; i < total_args; i++) { + Py_DECREF(args[i]); + } + Py_DECREF(callable); + if (res == NULL) { STACK_SHRINK(oparg); goto pop_2_error; } + STACK_SHRINK(oparg); + STACK_SHRINK(1); + stack_pointer[-1] = res; + break; + } + + case CALL_NO_KW_LEN: { + PyObject **args = (stack_pointer - oparg); + PyObject *callable = stack_pointer[-(1 + oparg)]; + PyObject *method = stack_pointer[-(2 + oparg)]; + PyObject *res; + assert(kwnames == NULL); + /* len(o) */ + int is_meth = method != NULL; + int total_args = oparg; + if (is_meth) { + callable = method; + args--; + total_args++; + } + DEOPT_IF(total_args != 1, CALL); + PyInterpreterState *interp = _PyInterpreterState_GET(); + DEOPT_IF(callable != interp->callable_cache.len, CALL); + STAT_INC(CALL, hit); + PyObject *arg = args[0]; + Py_ssize_t len_i = PyObject_Length(arg); + if (len_i < 0) { + goto error; + } + res = PyLong_FromSsize_t(len_i); + assert((res != NULL) ^ (_PyErr_Occurred(tstate) != NULL)); + + Py_DECREF(callable); + Py_DECREF(arg); + if (res == NULL) { STACK_SHRINK(oparg); goto pop_2_error; } + STACK_SHRINK(oparg); + STACK_SHRINK(1); + stack_pointer[-1] = res; + break; + } + + case CALL_NO_KW_ISINSTANCE: { + PyObject **args = (stack_pointer - oparg); + PyObject *callable = stack_pointer[-(1 + oparg)]; + PyObject *method = stack_pointer[-(2 + oparg)]; + PyObject *res; + assert(kwnames == NULL); + /* isinstance(o, o2) */ + int is_meth = method != NULL; + int total_args = oparg; + if (is_meth) { + callable = method; + args--; + total_args++; + } + DEOPT_IF(total_args != 2, CALL); + PyInterpreterState *interp = _PyInterpreterState_GET(); + DEOPT_IF(callable != interp->callable_cache.isinstance, CALL); + STAT_INC(CALL, hit); + PyObject *cls = args[1]; + PyObject *inst = args[0]; + int retval = PyObject_IsInstance(inst, cls); + if (retval < 0) { + goto error; + } + res = PyBool_FromLong(retval); + assert((res != NULL) ^ (_PyErr_Occurred(tstate) != NULL)); + + Py_DECREF(inst); + Py_DECREF(cls); + Py_DECREF(callable); + if (res == NULL) { STACK_SHRINK(oparg); goto pop_2_error; } + STACK_SHRINK(oparg); + STACK_SHRINK(1); + stack_pointer[-1] = res; + break; + } + + case CALL_NO_KW_METHOD_DESCRIPTOR_O: { + PyObject **args = (stack_pointer - oparg); + PyObject *method = stack_pointer[-(2 + oparg)]; + PyObject *res; + assert(kwnames == NULL); + int is_meth = method != NULL; + int total_args = oparg; + if (is_meth) { + args--; + total_args++; + } + PyMethodDescrObject *callable = + (PyMethodDescrObject *)PEEK(total_args + 1); + DEOPT_IF(total_args != 2, CALL); + DEOPT_IF(!Py_IS_TYPE(callable, &PyMethodDescr_Type), CALL); + PyMethodDef *meth = callable->d_method; + DEOPT_IF(meth->ml_flags != METH_O, CALL); + PyObject *arg = args[1]; + PyObject *self = args[0]; + DEOPT_IF(!Py_IS_TYPE(self, callable->d_common.d_type), CALL); + STAT_INC(CALL, hit); + PyCFunction cfunc = meth->ml_meth; + // This is slower but CPython promises to check all non-vectorcall + // function calls. + if (_Py_EnterRecursiveCallTstate(tstate, " while calling a Python object")) { + goto error; + } + res = _PyCFunction_TrampolineCall(cfunc, self, arg); + _Py_LeaveRecursiveCallTstate(tstate); + assert((res != NULL) ^ (_PyErr_Occurred(tstate) != NULL)); + Py_DECREF(self); + Py_DECREF(arg); + Py_DECREF(callable); + if (res == NULL) { STACK_SHRINK(oparg); goto pop_2_error; } + STACK_SHRINK(oparg); + STACK_SHRINK(1); + stack_pointer[-1] = res; + break; + } + + case CALL_METHOD_DESCRIPTOR_FAST_WITH_KEYWORDS: { + PyObject **args = (stack_pointer - oparg); + PyObject *method = stack_pointer[-(2 + oparg)]; + PyObject *res; + int is_meth = method != NULL; + int total_args = oparg; + if (is_meth) { + args--; + total_args++; + } + PyMethodDescrObject *callable = + (PyMethodDescrObject *)PEEK(total_args + 1); + DEOPT_IF(!Py_IS_TYPE(callable, &PyMethodDescr_Type), CALL); + PyMethodDef *meth = callable->d_method; + DEOPT_IF(meth->ml_flags != (METH_FASTCALL|METH_KEYWORDS), CALL); + PyTypeObject *d_type = callable->d_common.d_type; + PyObject *self = args[0]; + DEOPT_IF(!Py_IS_TYPE(self, d_type), CALL); + STAT_INC(CALL, hit); + int nargs = total_args - 1; + _PyCFunctionFastWithKeywords cfunc = + (_PyCFunctionFastWithKeywords)(void(*)(void))meth->ml_meth; + res = cfunc(self, args + 1, nargs - KWNAMES_LEN(), kwnames); + assert((res != NULL) ^ (_PyErr_Occurred(tstate) != NULL)); + kwnames = NULL; + + /* Free the arguments. */ + for (int i = 0; i < total_args; i++) { + Py_DECREF(args[i]); + } + Py_DECREF(callable); + if (res == NULL) { STACK_SHRINK(oparg); goto pop_2_error; } + STACK_SHRINK(oparg); + STACK_SHRINK(1); + stack_pointer[-1] = res; + break; + } + + case CALL_NO_KW_METHOD_DESCRIPTOR_NOARGS: { + PyObject **args = (stack_pointer - oparg); + PyObject *method = stack_pointer[-(2 + oparg)]; + PyObject *res; + assert(kwnames == NULL); + assert(oparg == 0 || oparg == 1); + int is_meth = method != NULL; + int total_args = oparg; + if (is_meth) { + args--; + total_args++; + } + DEOPT_IF(total_args != 1, CALL); + PyMethodDescrObject *callable = (PyMethodDescrObject *)SECOND(); + DEOPT_IF(!Py_IS_TYPE(callable, &PyMethodDescr_Type), CALL); + PyMethodDef *meth = callable->d_method; + PyObject *self = args[0]; + DEOPT_IF(!Py_IS_TYPE(self, callable->d_common.d_type), CALL); + DEOPT_IF(meth->ml_flags != METH_NOARGS, CALL); + STAT_INC(CALL, hit); + PyCFunction cfunc = meth->ml_meth; + // This is slower but CPython promises to check all non-vectorcall + // function calls. + if (_Py_EnterRecursiveCallTstate(tstate, " while calling a Python object")) { + goto error; + } + res = _PyCFunction_TrampolineCall(cfunc, self, NULL); + _Py_LeaveRecursiveCallTstate(tstate); + assert((res != NULL) ^ (_PyErr_Occurred(tstate) != NULL)); + Py_DECREF(self); + Py_DECREF(callable); + if (res == NULL) { STACK_SHRINK(oparg); goto pop_2_error; } + STACK_SHRINK(oparg); + STACK_SHRINK(1); + stack_pointer[-1] = res; + break; + } + + case CALL_NO_KW_METHOD_DESCRIPTOR_FAST: { + PyObject **args = (stack_pointer - oparg); + PyObject *method = stack_pointer[-(2 + oparg)]; + PyObject *res; + assert(kwnames == NULL); + int is_meth = method != NULL; + int total_args = oparg; + if (is_meth) { + args--; + total_args++; + } + PyMethodDescrObject *callable = + (PyMethodDescrObject *)PEEK(total_args + 1); + /* Builtin METH_FASTCALL methods, without keywords */ + DEOPT_IF(!Py_IS_TYPE(callable, &PyMethodDescr_Type), CALL); + PyMethodDef *meth = callable->d_method; + DEOPT_IF(meth->ml_flags != METH_FASTCALL, CALL); + PyObject *self = args[0]; + DEOPT_IF(!Py_IS_TYPE(self, callable->d_common.d_type), CALL); + STAT_INC(CALL, hit); + _PyCFunctionFast cfunc = + (_PyCFunctionFast)(void(*)(void))meth->ml_meth; + int nargs = total_args - 1; + res = cfunc(self, args + 1, nargs); + assert((res != NULL) ^ (_PyErr_Occurred(tstate) != NULL)); + /* Clear the stack of the arguments. */ + for (int i = 0; i < total_args; i++) { + Py_DECREF(args[i]); + } + Py_DECREF(callable); + if (res == NULL) { STACK_SHRINK(oparg); goto pop_2_error; } + STACK_SHRINK(oparg); + STACK_SHRINK(1); + stack_pointer[-1] = res; + break; + } + case MAKE_FUNCTION: { PyObject *codeobj = stack_pointer[-1]; PyObject *func; diff --git a/Python/optimizer.c b/Python/optimizer.c index 289b202f806ae1..f2a4d750630822 100644 --- a/Python/optimizer.c +++ b/Python/optimizer.c @@ -602,6 +602,20 @@ translate_bytecode_to_trace( } // End for (;;) done: + // Ensure the trace doesn't end in KW_NAMES + { + int i = trace_length - 1; + while (i >= 0 && trace[i].opcode == SAVE_IP) { + i--; + } + if (i >= 0 && trace[i].opcode == KW_NAMES) { + DPRINTF(2, "* Backing up over KW_NAMES\n"); + assert(i > 0); + assert(trace[i-1].opcode == SAVE_IP); + trace_length = i; + instr--; // Back up input too! + } + } // Skip short traces like SAVE_IP, LOAD_FAST, SAVE_IP, EXIT_TRACE if (trace_length > 3) { ADD_TO_TRACE(EXIT_TRACE, 0); diff --git a/Tools/cases_generator/generate_cases.py b/Tools/cases_generator/generate_cases.py index 6589289121863b..183baef78aa9a6 100644 --- a/Tools/cases_generator/generate_cases.py +++ b/Tools/cases_generator/generate_cases.py @@ -320,7 +320,6 @@ class ActiveCacheEffect: FORBIDDEN_NAMES_IN_UOPS = ( "resume_with_error", - "kwnames", "next_instr", "oparg1", # Proxy for super-instructions like LOAD_FAST_LOAD_FAST "JUMPBY", @@ -410,27 +409,32 @@ def __init__(self, inst: parser.InstDef): def is_viable_uop(self) -> bool: """Whether this instruction is viable as a uop.""" + if self.name.startswith("CALL"): + dprint: typing.Callable[..., None] = print + else: + dprint = lambda *args, **kwargs: None + if self.name == "EXIT_TRACE": return True # This has 'return frame' but it's okay if self.always_exits: - # print(f"Skipping {self.name} because it always exits") + dprint(f"Skipping {self.name} because it always exits") return False if self.instr_flags.HAS_ARG_FLAG: # If the instruction uses oparg, it cannot use any caches if self.active_caches: - # print(f"Skipping {self.name} because it uses oparg and caches") + dprint(f"Skipping {self.name} because it uses oparg and caches") return False else: # If it doesn't use oparg, it can have one cache entry if len(self.active_caches) > 1: - # print(f"Skipping {self.name} because it has >1 cache entries") + dprint(f"Skipping {self.name} because it has >1 cache entries") return False res = True for forbidden in FORBIDDEN_NAMES_IN_UOPS: # NOTE: To disallow unspecialized uops, use # if variable_used(self.inst, forbidden): if variable_used_unspecialized(self.inst, forbidden): - # print(f"Skipping {self.name} because it uses {forbidden}") + dprint(f"Skipping {self.name} because it uses {forbidden}") res = False return res @@ -1502,6 +1506,8 @@ def write_executor_instructions(self) -> None: with self.out.block(f"case {thing.name}:"): instr.write(self.out, tier=TIER_TWO) self.out.emit("break;") + # elif instr.kind != "op": + # print(f"NOTE: {thing.name} is not a viable uop") case parser.Macro(): pass case parser.Pseudo(): From a341d145c576bd52e930f3eac77097f14e0740fe Mon Sep 17 00:00:00 2001 From: Guido van Rossum Date: Fri, 14 Jul 2023 12:10:42 -0700 Subject: [PATCH 2/4] Don't actually allow using kwnames in Tier 2 But allow `assert(kwnames == NULL)`. --- Include/internal/pycore_opcode_metadata.h | 4 - Python/bytecodes.c | 30 ++--- Python/ceval.c | 4 +- Python/ceval_macros.h | 2 + Python/executor_cases.c.h | 139 ++-------------------- Python/generated_cases.c.h | 30 ++--- Tools/cases_generator/generate_cases.py | 1 + 7 files changed, 46 insertions(+), 164 deletions(-) diff --git a/Include/internal/pycore_opcode_metadata.h b/Include/internal/pycore_opcode_metadata.h index c252bffccce69e..39bc94cc133209 100644 --- a/Include/internal/pycore_opcode_metadata.h +++ b/Include/internal/pycore_opcode_metadata.h @@ -1309,19 +1309,15 @@ const struct opcode_macro_expansion _PyOpcode_macro_expansion[OPCODE_MACRO_EXPAN [GET_YIELD_FROM_ITER] = { .nuops = 1, .uops = { { GET_YIELD_FROM_ITER, 0, 0 } } }, [WITH_EXCEPT_START] = { .nuops = 1, .uops = { { WITH_EXCEPT_START, 0, 0 } } }, [PUSH_EXC_INFO] = { .nuops = 1, .uops = { { PUSH_EXC_INFO, 0, 0 } } }, - [KW_NAMES] = { .nuops = 1, .uops = { { KW_NAMES, 0, 0 } } }, [CALL_NO_KW_TYPE_1] = { .nuops = 1, .uops = { { CALL_NO_KW_TYPE_1, 0, 0 } } }, [CALL_NO_KW_STR_1] = { .nuops = 1, .uops = { { CALL_NO_KW_STR_1, 0, 0 } } }, [CALL_NO_KW_TUPLE_1] = { .nuops = 1, .uops = { { CALL_NO_KW_TUPLE_1, 0, 0 } } }, [EXIT_INIT_CHECK] = { .nuops = 1, .uops = { { EXIT_INIT_CHECK, 0, 0 } } }, - [CALL_BUILTIN_CLASS] = { .nuops = 1, .uops = { { CALL_BUILTIN_CLASS, 0, 0 } } }, [CALL_NO_KW_BUILTIN_O] = { .nuops = 1, .uops = { { CALL_NO_KW_BUILTIN_O, 0, 0 } } }, [CALL_NO_KW_BUILTIN_FAST] = { .nuops = 1, .uops = { { CALL_NO_KW_BUILTIN_FAST, 0, 0 } } }, - [CALL_BUILTIN_FAST_WITH_KEYWORDS] = { .nuops = 1, .uops = { { CALL_BUILTIN_FAST_WITH_KEYWORDS, 0, 0 } } }, [CALL_NO_KW_LEN] = { .nuops = 1, .uops = { { CALL_NO_KW_LEN, 0, 0 } } }, [CALL_NO_KW_ISINSTANCE] = { .nuops = 1, .uops = { { CALL_NO_KW_ISINSTANCE, 0, 0 } } }, [CALL_NO_KW_METHOD_DESCRIPTOR_O] = { .nuops = 1, .uops = { { CALL_NO_KW_METHOD_DESCRIPTOR_O, 0, 0 } } }, - [CALL_METHOD_DESCRIPTOR_FAST_WITH_KEYWORDS] = { .nuops = 1, .uops = { { CALL_METHOD_DESCRIPTOR_FAST_WITH_KEYWORDS, 0, 0 } } }, [CALL_NO_KW_METHOD_DESCRIPTOR_NOARGS] = { .nuops = 1, .uops = { { CALL_NO_KW_METHOD_DESCRIPTOR_NOARGS, 0, 0 } } }, [CALL_NO_KW_METHOD_DESCRIPTOR_FAST] = { .nuops = 1, .uops = { { CALL_NO_KW_METHOD_DESCRIPTOR_FAST, 0, 0 } } }, [MAKE_FUNCTION] = { .nuops = 1, .uops = { { MAKE_FUNCTION, 0, 0 } } }, diff --git a/Python/bytecodes.c b/Python/bytecodes.c index 3432b027713462..06fdf26d551c44 100644 --- a/Python/bytecodes.c +++ b/Python/bytecodes.c @@ -2788,7 +2788,7 @@ dummy_func( } inst(KW_NAMES, (--)) { - assert(kwnames == NULL); + ASSERT_KWNAMES_IS_NULL(); assert(oparg < PyTuple_GET_SIZE(FRAME_CO_CONSTS)); kwnames = GETITEM(FRAME_CO_CONSTS, oparg); } @@ -2940,7 +2940,7 @@ dummy_func( } inst(CALL_PY_EXACT_ARGS, (unused/1, func_version/2, method, callable, args[oparg] -- unused)) { - assert(kwnames == NULL); + ASSERT_KWNAMES_IS_NULL(); DEOPT_IF(tstate->interp->eval_frame, CALL); int is_meth = method != NULL; int argcount = oparg; @@ -2968,7 +2968,7 @@ dummy_func( } inst(CALL_PY_WITH_DEFAULTS, (unused/1, func_version/2, method, callable, args[oparg] -- unused)) { - assert(kwnames == NULL); + ASSERT_KWNAMES_IS_NULL(); DEOPT_IF(tstate->interp->eval_frame, CALL); int is_meth = method != NULL; int argcount = oparg; @@ -3006,7 +3006,7 @@ dummy_func( } inst(CALL_NO_KW_TYPE_1, (unused/1, unused/2, null, callable, args[oparg] -- res)) { - assert(kwnames == NULL); + ASSERT_KWNAMES_IS_NULL(); assert(oparg == 1); DEOPT_IF(null != NULL, CALL); PyObject *obj = args[0]; @@ -3018,7 +3018,7 @@ dummy_func( } inst(CALL_NO_KW_STR_1, (unused/1, unused/2, null, callable, args[oparg] -- res)) { - assert(kwnames == NULL); + ASSERT_KWNAMES_IS_NULL(); assert(oparg == 1); DEOPT_IF(null != NULL, CALL); DEOPT_IF(callable != (PyObject *)&PyUnicode_Type, CALL); @@ -3032,7 +3032,7 @@ dummy_func( } inst(CALL_NO_KW_TUPLE_1, (unused/1, unused/2, null, callable, args[oparg] -- res)) { - assert(kwnames == NULL); + ASSERT_KWNAMES_IS_NULL(); assert(oparg == 1); DEOPT_IF(null != NULL, CALL); DEOPT_IF(callable != (PyObject *)&PyTuple_Type, CALL); @@ -3051,7 +3051,7 @@ dummy_func( * 2. Pushes a shim frame to the frame stack (to cleanup after ``__init__``) * 3. Pushes the frame for ``__init__`` to the frame stack * */ - assert(kwnames == NULL); + ASSERT_KWNAMES_IS_NULL(); _PyCallCache *cache = (_PyCallCache *)next_instr; DEOPT_IF(null != NULL, CALL); DEOPT_IF(!PyType_Check(callable), CALL); @@ -3135,7 +3135,7 @@ dummy_func( inst(CALL_NO_KW_BUILTIN_O, (unused/1, unused/2, method, callable, args[oparg] -- res)) { /* Builtin METH_O functions */ - assert(kwnames == NULL); + ASSERT_KWNAMES_IS_NULL(); int is_meth = method != NULL; int total_args = oparg; if (is_meth) { @@ -3166,7 +3166,7 @@ dummy_func( inst(CALL_NO_KW_BUILTIN_FAST, (unused/1, unused/2, method, callable, args[oparg] -- res)) { /* Builtin METH_FASTCALL functions, without keywords */ - assert(kwnames == NULL); + ASSERT_KWNAMES_IS_NULL(); int is_meth = method != NULL; int total_args = oparg; if (is_meth) { @@ -3235,7 +3235,7 @@ dummy_func( } inst(CALL_NO_KW_LEN, (unused/1, unused/2, method, callable, args[oparg] -- res)) { - assert(kwnames == NULL); + ASSERT_KWNAMES_IS_NULL(); /* len(o) */ int is_meth = method != NULL; int total_args = oparg; @@ -3262,7 +3262,7 @@ dummy_func( } inst(CALL_NO_KW_ISINSTANCE, (unused/1, unused/2, method, callable, args[oparg] -- res)) { - assert(kwnames == NULL); + ASSERT_KWNAMES_IS_NULL(); /* isinstance(o, o2) */ int is_meth = method != NULL; int total_args = oparg; @@ -3292,7 +3292,7 @@ dummy_func( // This is secretly a super-instruction inst(CALL_NO_KW_LIST_APPEND, (unused/1, unused/2, method, self, args[oparg] -- unused)) { - assert(kwnames == NULL); + ASSERT_KWNAMES_IS_NULL(); assert(oparg == 1); assert(method != NULL); PyInterpreterState *interp = _PyInterpreterState_GET(); @@ -3312,7 +3312,7 @@ dummy_func( } inst(CALL_NO_KW_METHOD_DESCRIPTOR_O, (unused/1, unused/2, method, unused, args[oparg] -- res)) { - assert(kwnames == NULL); + ASSERT_KWNAMES_IS_NULL(); int is_meth = method != NULL; int total_args = oparg; if (is_meth) { @@ -3378,7 +3378,7 @@ dummy_func( } inst(CALL_NO_KW_METHOD_DESCRIPTOR_NOARGS, (unused/1, unused/2, method, unused, args[oparg] -- res)) { - assert(kwnames == NULL); + ASSERT_KWNAMES_IS_NULL(); assert(oparg == 0 || oparg == 1); int is_meth = method != NULL; int total_args = oparg; @@ -3410,7 +3410,7 @@ dummy_func( } inst(CALL_NO_KW_METHOD_DESCRIPTOR_FAST, (unused/1, unused/2, method, unused, args[oparg] -- res)) { - assert(kwnames == NULL); + ASSERT_KWNAMES_IS_NULL(); int is_meth = method != NULL; int total_args = oparg; if (is_meth) { diff --git a/Python/ceval.c b/Python/ceval.c index 146d7618538e20..f13ba9883d9814 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -2706,6 +2706,9 @@ void Py_LeaveRecursiveCall(void) ///////////////////// Experimental UOp Interpreter ///////////////////// +#undef ASSERT_KWNAMES_IS_NULL +#define ASSERT_KWNAMES_IS_NULL() (void)0 + #undef DEOPT_IF #define DEOPT_IF(COND, INSTNAME) \ if ((COND)) { \ @@ -2746,7 +2749,6 @@ _PyUopExecute(_PyExecutorObject *executor, _PyInterpreterFrame *frame, PyObject int opcode; uint64_t operand; int oparg; - PyObject *kwnames = NULL; // Borrowed reference. Reset by CALL instructions. for (;;) { opcode = self->trace[pc].opcode; diff --git a/Python/ceval_macros.h b/Python/ceval_macros.h index 72800aaaaa2ac4..874bd45becf0c9 100644 --- a/Python/ceval_macros.h +++ b/Python/ceval_macros.h @@ -349,3 +349,5 @@ static const convertion_func_ptr CONVERSION_FUNCTIONS[4] = { [FVC_REPR] = PyObject_Repr, [FVC_ASCII] = PyObject_ASCII }; + +#define ASSERT_KWNAMES_IS_NULL() assert(kwnames == NULL) diff --git a/Python/executor_cases.c.h b/Python/executor_cases.c.h index a0b492f025395d..d85e23b5abb8e6 100644 --- a/Python/executor_cases.c.h +++ b/Python/executor_cases.c.h @@ -1904,19 +1904,12 @@ break; } - case KW_NAMES: { - assert(kwnames == NULL); - assert(oparg < PyTuple_GET_SIZE(FRAME_CO_CONSTS)); - kwnames = GETITEM(FRAME_CO_CONSTS, oparg); - break; - } - case CALL_NO_KW_TYPE_1: { PyObject **args = (stack_pointer - oparg); PyObject *callable = stack_pointer[-(1 + oparg)]; PyObject *null = stack_pointer[-(2 + oparg)]; PyObject *res; - assert(kwnames == NULL); + ASSERT_KWNAMES_IS_NULL(); assert(oparg == 1); DEOPT_IF(null != NULL, CALL); PyObject *obj = args[0]; @@ -1936,7 +1929,7 @@ PyObject *callable = stack_pointer[-(1 + oparg)]; PyObject *null = stack_pointer[-(2 + oparg)]; PyObject *res; - assert(kwnames == NULL); + ASSERT_KWNAMES_IS_NULL(); assert(oparg == 1); DEOPT_IF(null != NULL, CALL); DEOPT_IF(callable != (PyObject *)&PyUnicode_Type, CALL); @@ -1957,7 +1950,7 @@ PyObject *callable = stack_pointer[-(1 + oparg)]; PyObject *null = stack_pointer[-(2 + oparg)]; PyObject *res; - assert(kwnames == NULL); + ASSERT_KWNAMES_IS_NULL(); assert(oparg == 1); DEOPT_IF(null != NULL, CALL); DEOPT_IF(callable != (PyObject *)&PyTuple_Type, CALL); @@ -1986,45 +1979,13 @@ break; } - case CALL_BUILTIN_CLASS: { - PyObject **args = (stack_pointer - oparg); - PyObject *callable = stack_pointer[-(1 + oparg)]; - PyObject *method = stack_pointer[-(2 + oparg)]; - PyObject *res; - int is_meth = method != NULL; - int total_args = oparg; - if (is_meth) { - callable = method; - args--; - total_args++; - } - int kwnames_len = KWNAMES_LEN(); - DEOPT_IF(!PyType_Check(callable), CALL); - PyTypeObject *tp = (PyTypeObject *)callable; - DEOPT_IF(tp->tp_vectorcall == NULL, CALL); - STAT_INC(CALL, hit); - res = tp->tp_vectorcall((PyObject *)tp, args, - total_args - kwnames_len, kwnames); - kwnames = NULL; - /* Free the arguments. */ - for (int i = 0; i < total_args; i++) { - Py_DECREF(args[i]); - } - Py_DECREF(tp); - if (res == NULL) { STACK_SHRINK(oparg); goto pop_2_error; } - STACK_SHRINK(oparg); - STACK_SHRINK(1); - stack_pointer[-1] = res; - break; - } - case CALL_NO_KW_BUILTIN_O: { PyObject **args = (stack_pointer - oparg); PyObject *callable = stack_pointer[-(1 + oparg)]; PyObject *method = stack_pointer[-(2 + oparg)]; PyObject *res; /* Builtin METH_O functions */ - assert(kwnames == NULL); + ASSERT_KWNAMES_IS_NULL(); int is_meth = method != NULL; int total_args = oparg; if (is_meth) { @@ -2062,7 +2023,7 @@ PyObject *method = stack_pointer[-(2 + oparg)]; PyObject *res; /* Builtin METH_FASTCALL functions, without keywords */ - assert(kwnames == NULL); + ASSERT_KWNAMES_IS_NULL(); int is_meth = method != NULL; int total_args = oparg; if (is_meth) { @@ -2098,54 +2059,12 @@ break; } - case CALL_BUILTIN_FAST_WITH_KEYWORDS: { - PyObject **args = (stack_pointer - oparg); - PyObject *callable = stack_pointer[-(1 + oparg)]; - PyObject *method = stack_pointer[-(2 + oparg)]; - PyObject *res; - /* Builtin METH_FASTCALL | METH_KEYWORDS functions */ - int is_meth = method != NULL; - int total_args = oparg; - if (is_meth) { - callable = method; - args--; - total_args++; - } - DEOPT_IF(!PyCFunction_CheckExact(callable), CALL); - DEOPT_IF(PyCFunction_GET_FLAGS(callable) != - (METH_FASTCALL | METH_KEYWORDS), CALL); - STAT_INC(CALL, hit); - /* res = func(self, args, nargs, kwnames) */ - _PyCFunctionFastWithKeywords cfunc = - (_PyCFunctionFastWithKeywords)(void(*)(void)) - PyCFunction_GET_FUNCTION(callable); - res = cfunc( - PyCFunction_GET_SELF(callable), - args, - total_args - KWNAMES_LEN(), - kwnames - ); - assert((res != NULL) ^ (_PyErr_Occurred(tstate) != NULL)); - kwnames = NULL; - - /* Free the arguments. */ - for (int i = 0; i < total_args; i++) { - Py_DECREF(args[i]); - } - Py_DECREF(callable); - if (res == NULL) { STACK_SHRINK(oparg); goto pop_2_error; } - STACK_SHRINK(oparg); - STACK_SHRINK(1); - stack_pointer[-1] = res; - break; - } - case CALL_NO_KW_LEN: { PyObject **args = (stack_pointer - oparg); PyObject *callable = stack_pointer[-(1 + oparg)]; PyObject *method = stack_pointer[-(2 + oparg)]; PyObject *res; - assert(kwnames == NULL); + ASSERT_KWNAMES_IS_NULL(); /* len(o) */ int is_meth = method != NULL; int total_args = oparg; @@ -2180,7 +2099,7 @@ PyObject *callable = stack_pointer[-(1 + oparg)]; PyObject *method = stack_pointer[-(2 + oparg)]; PyObject *res; - assert(kwnames == NULL); + ASSERT_KWNAMES_IS_NULL(); /* isinstance(o, o2) */ int is_meth = method != NULL; int total_args = oparg; @@ -2216,7 +2135,7 @@ PyObject **args = (stack_pointer - oparg); PyObject *method = stack_pointer[-(2 + oparg)]; PyObject *res; - assert(kwnames == NULL); + ASSERT_KWNAMES_IS_NULL(); int is_meth = method != NULL; int total_args = oparg; if (is_meth) { @@ -2252,49 +2171,11 @@ break; } - case CALL_METHOD_DESCRIPTOR_FAST_WITH_KEYWORDS: { - PyObject **args = (stack_pointer - oparg); - PyObject *method = stack_pointer[-(2 + oparg)]; - PyObject *res; - int is_meth = method != NULL; - int total_args = oparg; - if (is_meth) { - args--; - total_args++; - } - PyMethodDescrObject *callable = - (PyMethodDescrObject *)PEEK(total_args + 1); - DEOPT_IF(!Py_IS_TYPE(callable, &PyMethodDescr_Type), CALL); - PyMethodDef *meth = callable->d_method; - DEOPT_IF(meth->ml_flags != (METH_FASTCALL|METH_KEYWORDS), CALL); - PyTypeObject *d_type = callable->d_common.d_type; - PyObject *self = args[0]; - DEOPT_IF(!Py_IS_TYPE(self, d_type), CALL); - STAT_INC(CALL, hit); - int nargs = total_args - 1; - _PyCFunctionFastWithKeywords cfunc = - (_PyCFunctionFastWithKeywords)(void(*)(void))meth->ml_meth; - res = cfunc(self, args + 1, nargs - KWNAMES_LEN(), kwnames); - assert((res != NULL) ^ (_PyErr_Occurred(tstate) != NULL)); - kwnames = NULL; - - /* Free the arguments. */ - for (int i = 0; i < total_args; i++) { - Py_DECREF(args[i]); - } - Py_DECREF(callable); - if (res == NULL) { STACK_SHRINK(oparg); goto pop_2_error; } - STACK_SHRINK(oparg); - STACK_SHRINK(1); - stack_pointer[-1] = res; - break; - } - case CALL_NO_KW_METHOD_DESCRIPTOR_NOARGS: { PyObject **args = (stack_pointer - oparg); PyObject *method = stack_pointer[-(2 + oparg)]; PyObject *res; - assert(kwnames == NULL); + ASSERT_KWNAMES_IS_NULL(); assert(oparg == 0 || oparg == 1); int is_meth = method != NULL; int total_args = oparg; @@ -2332,7 +2213,7 @@ PyObject **args = (stack_pointer - oparg); PyObject *method = stack_pointer[-(2 + oparg)]; PyObject *res; - assert(kwnames == NULL); + ASSERT_KWNAMES_IS_NULL(); int is_meth = method != NULL; int total_args = oparg; if (is_meth) { diff --git a/Python/generated_cases.c.h b/Python/generated_cases.c.h index 68531dc074769e..2cbfafb012ec03 100644 --- a/Python/generated_cases.c.h +++ b/Python/generated_cases.c.h @@ -3466,7 +3466,7 @@ } TARGET(KW_NAMES) { - assert(kwnames == NULL); + ASSERT_KWNAMES_IS_NULL(); assert(oparg < PyTuple_GET_SIZE(FRAME_CO_CONSTS)); kwnames = GETITEM(FRAME_CO_CONSTS, oparg); DISPATCH(); @@ -3603,7 +3603,7 @@ PyObject *callable = stack_pointer[-(1 + oparg)]; PyObject *method = stack_pointer[-(2 + oparg)]; uint32_t func_version = read_u32(&next_instr[1].cache); - assert(kwnames == NULL); + ASSERT_KWNAMES_IS_NULL(); DEOPT_IF(tstate->interp->eval_frame, CALL); int is_meth = method != NULL; int argcount = oparg; @@ -3635,7 +3635,7 @@ PyObject *callable = stack_pointer[-(1 + oparg)]; PyObject *method = stack_pointer[-(2 + oparg)]; uint32_t func_version = read_u32(&next_instr[1].cache); - assert(kwnames == NULL); + ASSERT_KWNAMES_IS_NULL(); DEOPT_IF(tstate->interp->eval_frame, CALL); int is_meth = method != NULL; int argcount = oparg; @@ -3677,7 +3677,7 @@ PyObject *callable = stack_pointer[-(1 + oparg)]; PyObject *null = stack_pointer[-(2 + oparg)]; PyObject *res; - assert(kwnames == NULL); + ASSERT_KWNAMES_IS_NULL(); assert(oparg == 1); DEOPT_IF(null != NULL, CALL); PyObject *obj = args[0]; @@ -3698,7 +3698,7 @@ PyObject *callable = stack_pointer[-(1 + oparg)]; PyObject *null = stack_pointer[-(2 + oparg)]; PyObject *res; - assert(kwnames == NULL); + ASSERT_KWNAMES_IS_NULL(); assert(oparg == 1); DEOPT_IF(null != NULL, CALL); DEOPT_IF(callable != (PyObject *)&PyUnicode_Type, CALL); @@ -3721,7 +3721,7 @@ PyObject *callable = stack_pointer[-(1 + oparg)]; PyObject *null = stack_pointer[-(2 + oparg)]; PyObject *res; - assert(kwnames == NULL); + ASSERT_KWNAMES_IS_NULL(); assert(oparg == 1); DEOPT_IF(null != NULL, CALL); DEOPT_IF(callable != (PyObject *)&PyTuple_Type, CALL); @@ -3748,7 +3748,7 @@ * 2. Pushes a shim frame to the frame stack (to cleanup after ``__init__``) * 3. Pushes the frame for ``__init__`` to the frame stack * */ - assert(kwnames == NULL); + ASSERT_KWNAMES_IS_NULL(); _PyCallCache *cache = (_PyCallCache *)next_instr; DEOPT_IF(null != NULL, CALL); DEOPT_IF(!PyType_Check(callable), CALL); @@ -3848,7 +3848,7 @@ PyObject *method = stack_pointer[-(2 + oparg)]; PyObject *res; /* Builtin METH_O functions */ - assert(kwnames == NULL); + ASSERT_KWNAMES_IS_NULL(); int is_meth = method != NULL; int total_args = oparg; if (is_meth) { @@ -3888,7 +3888,7 @@ PyObject *method = stack_pointer[-(2 + oparg)]; PyObject *res; /* Builtin METH_FASTCALL functions, without keywords */ - assert(kwnames == NULL); + ASSERT_KWNAMES_IS_NULL(); int is_meth = method != NULL; int total_args = oparg; if (is_meth) { @@ -3975,7 +3975,7 @@ PyObject *callable = stack_pointer[-(1 + oparg)]; PyObject *method = stack_pointer[-(2 + oparg)]; PyObject *res; - assert(kwnames == NULL); + ASSERT_KWNAMES_IS_NULL(); /* len(o) */ int is_meth = method != NULL; int total_args = oparg; @@ -4011,7 +4011,7 @@ PyObject *callable = stack_pointer[-(1 + oparg)]; PyObject *method = stack_pointer[-(2 + oparg)]; PyObject *res; - assert(kwnames == NULL); + ASSERT_KWNAMES_IS_NULL(); /* isinstance(o, o2) */ int is_meth = method != NULL; int total_args = oparg; @@ -4048,7 +4048,7 @@ PyObject **args = (stack_pointer - oparg); PyObject *self = stack_pointer[-(1 + oparg)]; PyObject *method = stack_pointer[-(2 + oparg)]; - assert(kwnames == NULL); + ASSERT_KWNAMES_IS_NULL(); assert(oparg == 1); assert(method != NULL); PyInterpreterState *interp = _PyInterpreterState_GET(); @@ -4071,7 +4071,7 @@ PyObject **args = (stack_pointer - oparg); PyObject *method = stack_pointer[-(2 + oparg)]; PyObject *res; - assert(kwnames == NULL); + ASSERT_KWNAMES_IS_NULL(); int is_meth = method != NULL; int total_args = oparg; if (is_meth) { @@ -4153,7 +4153,7 @@ PyObject **args = (stack_pointer - oparg); PyObject *method = stack_pointer[-(2 + oparg)]; PyObject *res; - assert(kwnames == NULL); + ASSERT_KWNAMES_IS_NULL(); assert(oparg == 0 || oparg == 1); int is_meth = method != NULL; int total_args = oparg; @@ -4193,7 +4193,7 @@ PyObject **args = (stack_pointer - oparg); PyObject *method = stack_pointer[-(2 + oparg)]; PyObject *res; - assert(kwnames == NULL); + ASSERT_KWNAMES_IS_NULL(); int is_meth = method != NULL; int total_args = oparg; if (is_meth) { diff --git a/Tools/cases_generator/generate_cases.py b/Tools/cases_generator/generate_cases.py index 183baef78aa9a6..07a91c3013c571 100644 --- a/Tools/cases_generator/generate_cases.py +++ b/Tools/cases_generator/generate_cases.py @@ -320,6 +320,7 @@ class ActiveCacheEffect: FORBIDDEN_NAMES_IN_UOPS = ( "resume_with_error", + "kwnames", "next_instr", "oparg1", # Proxy for super-instructions like LOAD_FAST_LOAD_FAST "JUMPBY", From 35a1bb72a1463946e707acc13c17b26e8a9f7747 Mon Sep 17 00:00:00 2001 From: Guido van Rossum Date: Mon, 17 Jul 2023 10:17:35 -0700 Subject: [PATCH 3/4] Remove unneeded block --- Python/optimizer.c | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/Python/optimizer.c b/Python/optimizer.c index f2a4d750630822..289b202f806ae1 100644 --- a/Python/optimizer.c +++ b/Python/optimizer.c @@ -602,20 +602,6 @@ translate_bytecode_to_trace( } // End for (;;) done: - // Ensure the trace doesn't end in KW_NAMES - { - int i = trace_length - 1; - while (i >= 0 && trace[i].opcode == SAVE_IP) { - i--; - } - if (i >= 0 && trace[i].opcode == KW_NAMES) { - DPRINTF(2, "* Backing up over KW_NAMES\n"); - assert(i > 0); - assert(trace[i-1].opcode == SAVE_IP); - trace_length = i; - instr--; // Back up input too! - } - } // Skip short traces like SAVE_IP, LOAD_FAST, SAVE_IP, EXIT_TRACE if (trace_length > 3) { ADD_TO_TRACE(EXIT_TRACE, 0); From eacb08693dee77a6f58fa98ce3d5c59de40ad5ab Mon Sep 17 00:00:00 2001 From: Guido van Rossum Date: Mon, 17 Jul 2023 10:21:28 -0700 Subject: [PATCH 4/4] Drop debug output --- Tools/cases_generator/generate_cases.py | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/Tools/cases_generator/generate_cases.py b/Tools/cases_generator/generate_cases.py index 7e94112da466bf..112f29a83e4c10 100644 --- a/Tools/cases_generator/generate_cases.py +++ b/Tools/cases_generator/generate_cases.py @@ -408,10 +408,9 @@ def __init__(self, inst: parser.InstDef): def is_viable_uop(self) -> bool: """Whether this instruction is viable as a uop.""" - if self.name.startswith("CALL"): - dprint: typing.Callable[..., None] = print - else: - dprint = lambda *args, **kwargs: None + dprint: typing.Callable[..., None] = lambda *args, **kwargs: None + # if self.name.startswith("CALL"): + # dprint = print if self.name == "EXIT_TRACE": return True # This has 'return frame' but it's okay pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy