From a23ec47f10d6337d9abeefe655943f38b7aa8f01 Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Tue, 27 Sep 2016 11:37:10 +0300 Subject: Issue #27703: Got rid of unnecessary NULL checks in do_raise() in release mode. Patch by Xiang Zhang. --- Python/ceval.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) (limited to 'Python/ceval.c') diff --git a/Python/ceval.c b/Python/ceval.c index 39cf33019d..a09de05ebe 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -4245,6 +4245,9 @@ do_raise(PyObject *exc, PyObject *cause) goto raise_error; } + assert(type != NULL); + assert(value != NULL); + if (cause) { PyObject *fixed_cause; if (PyExceptionClass_Check(cause)) { @@ -4271,8 +4274,8 @@ do_raise(PyObject *exc, PyObject *cause) PyErr_SetObject(type, value); /* PyErr_SetObject incref's its arguments */ - Py_XDECREF(value); - Py_XDECREF(type); + Py_DECREF(value); + Py_DECREF(type); return 0; raise_error: -- cgit v1.2.1 From 186ac5fccc365207688ace236a90ec92b54d3a61 Mon Sep 17 00:00:00 2001 From: Raymond Hettinger Date: Sat, 15 Oct 2016 19:03:06 -0700 Subject: Minor fix-up to apply the stack adjustment macros consistent with the other opcodes --- Python/ceval.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'Python/ceval.c') diff --git a/Python/ceval.c b/Python/ceval.c index 3e74819c90..b50ad3c263 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -1538,7 +1538,7 @@ _PyEval_EvalFrameDefault(PyFrameObject *f, int throwflag) TARGET(SET_ADD) { PyObject *v = POP(); - PyObject *set = stack_pointer[-oparg]; + PyObject *set = PEEK(oparg); int err; err = PySet_Add(set, v); Py_DECREF(v); @@ -2796,7 +2796,7 @@ _PyEval_EvalFrameDefault(PyFrameObject *f, int throwflag) PyObject *map; int err; STACKADJ(-2); - map = stack_pointer[-oparg]; /* dict */ + map = PEEK(oparg); /* dict */ assert(PyDict_CheckExact(map)); err = PyDict_SetItem(map, key, value); /* map[key] = value */ Py_DECREF(value); -- cgit v1.2.1 From bf3d60ac08f4b02d96e26e8b2182b2dc20fb8e87 Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Thu, 20 Oct 2016 12:18:10 +0200 Subject: Issue #21955: Please don't try to optimize int+int --- Python/ceval.c | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'Python/ceval.c') diff --git a/Python/ceval.c b/Python/ceval.c index b50ad3c263..2df94cfa80 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -1424,6 +1424,12 @@ _PyEval_EvalFrameDefault(PyFrameObject *f, int throwflag) PyObject *right = POP(); PyObject *left = TOP(); PyObject *sum; + /* NOTE(haypo): Please don't try to micro-optimize int+int on + CPython using bytecode, it is simply worthless. + See http://bugs.python.org/issue21955 and + http://bugs.python.org/issue10044 for the discussion. In short, + no patch shown any impact on a realistic benchmark, only a minor + speedup on microbenchmarks. */ if (PyUnicode_CheckExact(left) && PyUnicode_CheckExact(right)) { sum = unicode_concatenate(left, right, f, next_instr); -- cgit v1.2.1 From 18dea7d67d8b263dd8b49e1d44dcb09ca86ad08a Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Fri, 11 Nov 2016 02:13:35 +0100 Subject: Issue #28618: Make hot functions using __attribute__((hot)) When Python is not compiled with PGO, the performance of Python on call_simple and call_method microbenchmarks depend highly on the code placement. In the worst case, the performance slowdown can be up to 70%. The GCC __attribute__((hot)) attribute helps to keep hot code close to reduce the risk of such major slowdown. This attribute is ignored when Python is compiled with PGO. The following functions are considered as hot according to statistics collected by perf record/perf report: * _PyEval_EvalFrameDefault() * call_function() * _PyFunction_FastCall() * PyFrame_New() * frame_dealloc() * PyErr_Occurred() --- Python/ceval.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'Python/ceval.c') diff --git a/Python/ceval.c b/Python/ceval.c index 6cc6dfc744..d08b316db4 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -718,7 +718,7 @@ PyEval_EvalFrameEx(PyFrameObject *f, int throwflag) return tstate->interp->eval_frame(f, throwflag); } -PyObject * +PyObject* _Py_HOT_FUNCTION _PyEval_EvalFrameDefault(PyFrameObject *f, int throwflag) { #ifdef DXPAIRS @@ -4771,7 +4771,7 @@ if (tstate->use_tracing && tstate->c_profilefunc) { \ x = call; \ } -static PyObject * +static PyObject* _Py_HOT_FUNCTION call_function(PyObject ***pp_stack, Py_ssize_t oparg, PyObject *kwnames) { PyObject **pfunc = (*pp_stack) - oparg - 1; @@ -4844,7 +4844,7 @@ call_function(PyObject ***pp_stack, Py_ssize_t oparg, PyObject *kwnames) done before evaluating the frame. */ -static PyObject* +static PyObject* _Py_HOT_FUNCTION _PyFunction_FastCall(PyCodeObject *co, PyObject **args, Py_ssize_t nargs, PyObject *globals) { -- cgit v1.2.1 From b929812ae229729980606ce9fab6ddc4f9fa56ca Mon Sep 17 00:00:00 2001 From: Raymond Hettinger Date: Fri, 11 Nov 2016 04:32:11 -0800 Subject: merge --- Python/ceval.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'Python/ceval.c') diff --git a/Python/ceval.c b/Python/ceval.c index d08b316db4..fe562a8450 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -2468,8 +2468,9 @@ _PyEval_EvalFrameDefault(PyFrameObject *f, int throwflag) TARGET(STORE_DEREF) { PyObject *v = POP(); PyObject *cell = freevars[oparg]; - PyCell_Set(cell, v); - Py_DECREF(v); + PyObject *oldobj = PyCell_GET(cell); + PyCell_SET(cell, v); + Py_XDECREF(oldobj); DISPATCH(); } -- cgit v1.2.1 From 373079de88784b082587c0d18a1e5fb18efea95e Mon Sep 17 00:00:00 2001 From: Raymond Hettinger Date: Sat, 12 Nov 2016 04:10:35 -0500 Subject: Issue #28665: Use macro form of PyCell_GET/SET --- Python/ceval.c | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) (limited to 'Python/ceval.c') diff --git a/Python/ceval.c b/Python/ceval.c index fe562a8450..eefe66f6cd 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -2405,8 +2405,10 @@ _PyEval_EvalFrameDefault(PyFrameObject *f, int throwflag) TARGET(DELETE_DEREF) { PyObject *cell = freevars[oparg]; - if (PyCell_GET(cell) != NULL) { - PyCell_Set(cell, NULL); + PyObject *oldobj = PyCell_GET(cell); + if (oldobj != NULL) { + PyCell_SET(cell, NULL); + Py_DECREF(oldobj); DISPATCH(); } format_exc_unbound(co, oparg); @@ -5351,8 +5353,10 @@ unicode_concatenate(PyObject *v, PyObject *w, PyObject **freevars = (f->f_localsplus + f->f_code->co_nlocals); PyObject *c = freevars[oparg]; - if (PyCell_GET(c) == v) - PyCell_Set(c, NULL); + if (PyCell_GET(c) == v) { + PyCell_SET(c, NULL); + Py_DECREF(v); + } break; } case STORE_NAME: -- cgit v1.2.1 From 5d858465b6ebdafa05f86309457848cc26913b6a Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Sun, 20 Nov 2016 10:16:47 +0200 Subject: Added the const qualifier to char* variables that refer to readonly internal UTF-8 represenatation of Unicode objects. --- Python/ceval.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'Python/ceval.c') diff --git a/Python/ceval.c b/Python/ceval.c index bbbf005f00..b08427d7bd 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -5440,8 +5440,8 @@ _PyEval_RequestCodeExtraIndex(freefunc free) static void dtrace_function_entry(PyFrameObject *f) { - char* filename; - char* funcname; + const char *filename; + const char *funcname; int lineno; filename = PyUnicode_AsUTF8(f->f_code->co_filename); @@ -5454,8 +5454,8 @@ dtrace_function_entry(PyFrameObject *f) static void dtrace_function_return(PyFrameObject *f) { - char* filename; - char* funcname; + const char *filename; + const char *funcname; int lineno; filename = PyUnicode_AsUTF8(f->f_code->co_filename); @@ -5471,7 +5471,7 @@ maybe_dtrace_line(PyFrameObject *frame, int *instr_lb, int *instr_ub, int *instr_prev) { int line = frame->f_lineno; - char *co_filename, *co_name; + const char *co_filename, *co_name; /* If the last instruction executed isn't in the current instruction window, reset the window. -- cgit v1.2.1 From 1078ad8ce638fcbe0084c63774f78679cc15b30a Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Mon, 28 Nov 2016 11:59:04 +0100 Subject: Remove CALL_PROFILE special build Issue #28799: * Remove the PyEval_GetCallStats() function. * Deprecate the untested and undocumented sys.callstats() function. * Remove the CALL_PROFILE special build Use the sys.setprofile() function, cProfile or profile module to profile function calls. --- Python/ceval.c | 91 ---------------------------------------------------------- 1 file changed, 91 deletions(-) (limited to 'Python/ceval.c') diff --git a/Python/ceval.c b/Python/ceval.c index ca9914c866..569c609979 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -83,63 +83,6 @@ static long dxp[256]; #endif #endif -/* Function call profile */ -#ifdef CALL_PROFILE -#define PCALL_NUM 11 -static int pcall[PCALL_NUM]; - -#define PCALL_ALL 0 -#define PCALL_FUNCTION 1 -#define PCALL_FAST_FUNCTION 2 -#define PCALL_FASTER_FUNCTION 3 -#define PCALL_METHOD 4 -#define PCALL_BOUND_METHOD 5 -#define PCALL_CFUNCTION 6 -#define PCALL_TYPE 7 -#define PCALL_GENERATOR 8 -#define PCALL_OTHER 9 -#define PCALL_POP 10 - -/* Notes about the statistics - - PCALL_FAST stats - - FAST_FUNCTION means no argument tuple needs to be created. - FASTER_FUNCTION means that the fast-path frame setup code is used. - - If there is a method call where the call can be optimized by changing - the argument tuple and calling the function directly, it gets recorded - twice. - - As a result, the relationship among the statistics appears to be - PCALL_ALL == PCALL_FUNCTION + PCALL_METHOD - PCALL_BOUND_METHOD + - PCALL_CFUNCTION + PCALL_TYPE + PCALL_GENERATOR + PCALL_OTHER - PCALL_FUNCTION > PCALL_FAST_FUNCTION > PCALL_FASTER_FUNCTION - PCALL_METHOD > PCALL_BOUND_METHOD -*/ - -#define PCALL(POS) pcall[POS]++ - -PyObject * -PyEval_GetCallStats(PyObject *self) -{ - return Py_BuildValue("iiiiiiiiiii", - pcall[0], pcall[1], pcall[2], pcall[3], - pcall[4], pcall[5], pcall[6], pcall[7], - pcall[8], pcall[9], pcall[10]); -} -#else -#define PCALL(O) - -PyObject * -PyEval_GetCallStats(PyObject *self) -{ - Py_INCREF(Py_None); - return Py_None; -} -#endif - - #ifdef WITH_THREAD #define GIL_REQUEST _Py_atomic_load_relaxed(&gil_drop_request) #else @@ -3278,7 +3221,6 @@ _PyEval_EvalFrameDefault(PyFrameObject *f, int throwflag) PREDICTED(CALL_FUNCTION); TARGET(CALL_FUNCTION) { PyObject **sp, *res; - PCALL(PCALL_ALL); sp = stack_pointer; res = call_function(&sp, oparg, NULL); stack_pointer = sp; @@ -3294,7 +3236,6 @@ _PyEval_EvalFrameDefault(PyFrameObject *f, int throwflag) names = POP(); assert(PyTuple_CheckExact(names) && PyTuple_GET_SIZE(names) <= oparg); - PCALL(PCALL_ALL); sp = stack_pointer; res = call_function(&sp, oparg, names); stack_pointer = sp; @@ -3309,7 +3250,6 @@ _PyEval_EvalFrameDefault(PyFrameObject *f, int throwflag) TARGET(CALL_FUNCTION_EX) { PyObject *func, *callargs, *kwargs = NULL, *result; - PCALL(PCALL_ALL); if (oparg & 0x01) { kwargs = POP(); if (!PyDict_CheckExact(kwargs)) { @@ -4099,8 +4039,6 @@ _PyEval_EvalCodeWithName(PyObject *_co, PyObject *globals, PyObject *locals, * when the generator is resumed. */ Py_CLEAR(f->f_back); - PCALL(PCALL_GENERATOR); - /* Create a new generator that owns the ready to run frame * and return that as the value. */ if (is_coro) { @@ -4793,8 +4731,6 @@ call_function(PyObject ***pp_stack, Py_ssize_t oparg, PyObject *kwnames) if (PyCFunction_Check(func)) { PyThreadState *tstate = PyThreadState_GET(); - PCALL(PCALL_CFUNCTION); - stack = (*pp_stack) - nargs - nkwargs; C_TRACE(x, _PyCFunction_FastCallKeywords(func, stack, nargs, kwnames)); } @@ -4802,8 +4738,6 @@ call_function(PyObject ***pp_stack, Py_ssize_t oparg, PyObject *kwnames) if (PyMethod_Check(func) && PyMethod_GET_SELF(func) != NULL) { /* optimize access to bound methods */ PyObject *self = PyMethod_GET_SELF(func); - PCALL(PCALL_METHOD); - PCALL(PCALL_BOUND_METHOD); Py_INCREF(self); func = PyMethod_GET_FUNCTION(func); Py_INCREF(func); @@ -4835,7 +4769,6 @@ call_function(PyObject ***pp_stack, Py_ssize_t oparg, PyObject *kwnames) while ((*pp_stack) > pfunc) { w = EXT_POP(*pp_stack); Py_DECREF(w); - PCALL(PCALL_POP); } return x; @@ -4860,7 +4793,6 @@ _PyFunction_FastCall(PyCodeObject *co, PyObject **args, Py_ssize_t nargs, Py_ssize_t i; PyObject *result; - PCALL(PCALL_FASTER_FUNCTION); assert(globals != NULL); /* XXX Perhaps we should create a specialized PyFrame_New() that doesn't take locals, but does @@ -4906,9 +4838,6 @@ fast_function(PyObject *func, PyObject **stack, /* kwnames must only contains str strings, no subclass, and all keys must be unique */ - PCALL(PCALL_FUNCTION); - PCALL(PCALL_FAST_FUNCTION); - if (co->co_kwonlyargcount == 0 && nkwargs == 0 && co->co_flags == (CO_OPTIMIZED | CO_NEWLOCALS | CO_NOFREE)) { @@ -4971,9 +4900,6 @@ _PyFunction_FastCallDict(PyObject *func, PyObject **args, Py_ssize_t nargs, assert(nargs == 0 || args != NULL); assert(kwargs == NULL || PyDict_Check(kwargs)); - PCALL(PCALL_FUNCTION); - PCALL(PCALL_FAST_FUNCTION); - if (co->co_kwonlyargcount == 0 && (kwargs == NULL || PyDict_Size(kwargs) == 0) && co->co_flags == (CO_OPTIMIZED | CO_NEWLOCALS | CO_NOFREE)) @@ -5041,23 +4967,6 @@ _PyFunction_FastCallDict(PyObject *func, PyObject **args, Py_ssize_t nargs, static PyObject * do_call_core(PyObject *func, PyObject *callargs, PyObject *kwdict) { -#ifdef CALL_PROFILE - /* At this point, we have to look at the type of func to - update the call stats properly. Do it here so as to avoid - exposing the call stats machinery outside ceval.c - */ - if (PyFunction_Check(func)) - PCALL(PCALL_FUNCTION); - else if (PyMethod_Check(func)) - PCALL(PCALL_METHOD); - else if (PyType_Check(func)) - PCALL(PCALL_TYPE); - else if (PyCFunction_Check(func)) - PCALL(PCALL_CFUNCTION); - else - PCALL(PCALL_OTHER); -#endif - if (PyCFunction_Check(func)) { PyObject *result; PyThreadState *tstate = PyThreadState_GET(); -- cgit v1.2.1 From cbaccc9bc862045167296cabcab79a152d5e64fd Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Mon, 28 Nov 2016 18:32:31 +0100 Subject: call_function(): document PyMethod optimization --- Python/ceval.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) (limited to 'Python/ceval.c') diff --git a/Python/ceval.c b/Python/ceval.c index 569c609979..42c0c614b1 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -4736,7 +4736,11 @@ call_function(PyObject ***pp_stack, Py_ssize_t oparg, PyObject *kwnames) } else { if (PyMethod_Check(func) && PyMethod_GET_SELF(func) != NULL) { - /* optimize access to bound methods */ + /* Optimize access to bound methods. Reuse the Python stack + to pass 'self' as the first argument, replace 'func' + with 'self'. It avoids the creation of a new temporary tuple + for arguments (to replace func with self) when the method uses + FASTCALL. */ PyObject *self = PyMethod_GET_SELF(func); Py_INCREF(self); func = PyMethod_GET_FUNCTION(func); -- cgit v1.2.1 From 366f4ee4da4420842eac26b3fa00c2b01d4515a6 Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Thu, 1 Dec 2016 14:43:22 +0100 Subject: Replace PyObject_CallFunctionObjArgs() with fastcall * PyObject_CallFunctionObjArgs(func, NULL) => _PyObject_CallNoArg(func) * PyObject_CallFunctionObjArgs(func, arg, NULL) => _PyObject_CallArg1(func, arg) PyObject_CallFunctionObjArgs() allocates 40 bytes on the C stack and requires extra work to "parse" C arguments to build a C array of PyObject*. _PyObject_CallNoArg() and _PyObject_CallArg1() are simpler and don't allocate memory on the C stack. This change is part of the fastcall project. The change on listsort() is related to the issue #23507. --- Python/ceval.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'Python/ceval.c') diff --git a/Python/ceval.c b/Python/ceval.c index 42c0c614b1..d4a90c4fc9 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -1756,7 +1756,7 @@ _PyEval_EvalFrameDefault(PyFrameObject *f, int throwflag) Py_DECREF(value); goto error; } - res = PyObject_CallFunctionObjArgs(hook, value, NULL); + res = _PyObject_CallArg1(hook, value); Py_DECREF(value); if (res == NULL) goto error; @@ -3062,7 +3062,7 @@ _PyEval_EvalFrameDefault(PyFrameObject *f, int throwflag) Py_DECREF(mgr); if (enter == NULL) goto error; - res = PyObject_CallFunctionObjArgs(enter, NULL); + res = _PyObject_CallNoArg(enter); Py_DECREF(enter); if (res == NULL) goto error; @@ -3096,7 +3096,7 @@ _PyEval_EvalFrameDefault(PyFrameObject *f, int throwflag) } SET_TOP(exit); Py_DECREF(mgr); - res = PyObject_CallFunctionObjArgs(enter, NULL); + res = _PyObject_CallNoArg(enter); Py_DECREF(enter); if (res == NULL) goto error; -- cgit v1.2.1 From 4bfba3d29e12e5ccb20fa324b76b7c19850fd7c4 Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Thu, 1 Dec 2016 14:45:31 +0100 Subject: WITH_CLEANUP_START uses fastcall Modify WITH_CLEANUP_START bytecode: replace PyObject_CallFunctionObjArgs() with _PyObject_FastCall(). --- Python/ceval.c | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) (limited to 'Python/ceval.c') diff --git a/Python/ceval.c b/Python/ceval.c index d4a90c4fc9..b615bd9455 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -3135,8 +3135,12 @@ _PyEval_EvalFrameDefault(PyFrameObject *f, int throwflag) gotos should still be resumed.) */ + PyObject* stack[3]; PyObject *exit_func; - PyObject *exc = TOP(), *val = Py_None, *tb = Py_None, *res; + PyObject *exc, *val, *tb, *res; + + val = tb = Py_None; + exc = TOP(); if (exc == Py_None) { (void)POP(); exit_func = TOP(); @@ -3180,8 +3184,11 @@ _PyEval_EvalFrameDefault(PyFrameObject *f, int throwflag) assert(block->b_type == EXCEPT_HANDLER); block->b_level--; } - /* XXX Not the fastest way to call it... */ - res = PyObject_CallFunctionObjArgs(exit_func, exc, val, tb, NULL); + + stack[0] = exc; + stack[1] = val; + stack[2] = tb; + res = _PyObject_FastCall(exit_func, stack, 3); Py_DECREF(exit_func); if (res == NULL) goto error; -- cgit v1.2.1 From 39ae5bedd90f9caf9a78efa82f4d11e838933b3a Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Sun, 4 Dec 2016 22:59:09 +0100 Subject: Backed out changeset b9c9691c72c5 Issue #28858: The change b9c9691c72c5 introduced a regression. It seems like _PyObject_CallArg1() uses more stack memory than PyObject_CallFunctionObjArgs(). --- Python/ceval.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'Python/ceval.c') diff --git a/Python/ceval.c b/Python/ceval.c index b615bd9455..ca876e0ba8 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -1756,7 +1756,7 @@ _PyEval_EvalFrameDefault(PyFrameObject *f, int throwflag) Py_DECREF(value); goto error; } - res = _PyObject_CallArg1(hook, value); + res = PyObject_CallFunctionObjArgs(hook, value, NULL); Py_DECREF(value); if (res == NULL) goto error; @@ -3062,7 +3062,7 @@ _PyEval_EvalFrameDefault(PyFrameObject *f, int throwflag) Py_DECREF(mgr); if (enter == NULL) goto error; - res = _PyObject_CallNoArg(enter); + res = PyObject_CallFunctionObjArgs(enter, NULL); Py_DECREF(enter); if (res == NULL) goto error; @@ -3096,7 +3096,7 @@ _PyEval_EvalFrameDefault(PyFrameObject *f, int throwflag) } SET_TOP(exit); Py_DECREF(mgr); - res = _PyObject_CallNoArg(enter); + res = PyObject_CallFunctionObjArgs(enter, NULL); Py_DECREF(enter); if (res == NULL) goto error; -- cgit v1.2.1 From 647e75b25047cd0bf6a780e929db7a67de4cca75 Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Tue, 6 Dec 2016 16:27:24 +0100 Subject: Uniformize argument names of "call" functions Issue #28838: Rename parameters of the "calls" functions of the Python C API. * Rename 'callable_object' and 'func' to 'callable': any Python callable object is accepted, not only Python functions * Rename 'method' and 'nameid' to 'name' (method name) * Rename 'o' to 'obj' * Move, fix and update documentation of PyObject_CallXXX() functions in abstract.h * Update also the documentaton of the C API (update parameter names) --- Python/ceval.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) (limited to 'Python/ceval.c') diff --git a/Python/ceval.c b/Python/ceval.c index ca876e0ba8..8b17c09ca1 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -4637,7 +4637,8 @@ PyEval_MergeCompilerFlags(PyCompilerFlags *cf) The arg must be a tuple or NULL. The kw must be a dict or NULL. */ PyObject * -PyEval_CallObjectWithKeywords(PyObject *func, PyObject *args, PyObject *kwargs) +PyEval_CallObjectWithKeywords(PyObject *callable, + PyObject *args, PyObject *kwargs) { #ifdef Py_DEBUG /* PyEval_CallObjectWithKeywords() must not be called with an exception @@ -4647,7 +4648,7 @@ PyEval_CallObjectWithKeywords(PyObject *func, PyObject *args, PyObject *kwargs) #endif if (args == NULL) { - return _PyObject_FastCallDict(func, NULL, 0, kwargs); + return _PyObject_FastCallDict(callable, NULL, 0, kwargs); } if (!PyTuple_Check(args)) { @@ -4662,7 +4663,7 @@ PyEval_CallObjectWithKeywords(PyObject *func, PyObject *args, PyObject *kwargs) return NULL; } - return PyObject_Call(func, args, kwargs); + return PyObject_Call(callable, args, kwargs); } const char * -- cgit v1.2.1 From b729181a504b6aff2461dcbc07c33e902ee90cb0 Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Tue, 6 Dec 2016 18:45:50 +0100 Subject: Use _PyObject_CallNoArg() Replace: PyObject_CallObject(callable, NULL) with: _PyObject_CallNoArg(callable) --- Python/ceval.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'Python/ceval.c') diff --git a/Python/ceval.c b/Python/ceval.c index 8b17c09ca1..8420aec3b9 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -4198,7 +4198,7 @@ do_raise(PyObject *exc, PyObject *cause) if (PyExceptionClass_Check(exc)) { type = exc; - value = PyObject_CallObject(exc, NULL); + value = _PyObject_CallNoArg(exc); if (value == NULL) goto raise_error; if (!PyExceptionInstance_Check(value)) { @@ -4229,7 +4229,7 @@ do_raise(PyObject *exc, PyObject *cause) if (cause) { PyObject *fixed_cause; if (PyExceptionClass_Check(cause)) { - fixed_cause = PyObject_CallObject(cause, NULL); + fixed_cause = _PyObject_CallNoArg(cause); if (fixed_cause == NULL) goto raise_error; Py_DECREF(cause); -- cgit v1.2.1 From a95a307d2cbb7fc1c778691f142e0c21c05fd5bc Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Tue, 6 Dec 2016 18:46:19 +0100 Subject: Use _PyObject_CallNoArg() Replace: PyObject_CallFunctionObjArgs(callable, NULL) with: _PyObject_CallNoArg(callable) --- Python/ceval.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'Python/ceval.c') diff --git a/Python/ceval.c b/Python/ceval.c index 8420aec3b9..c6c6e05650 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -3062,7 +3062,7 @@ _PyEval_EvalFrameDefault(PyFrameObject *f, int throwflag) Py_DECREF(mgr); if (enter == NULL) goto error; - res = PyObject_CallFunctionObjArgs(enter, NULL); + res = _PyObject_CallNoArg(enter); Py_DECREF(enter); if (res == NULL) goto error; @@ -3096,7 +3096,7 @@ _PyEval_EvalFrameDefault(PyFrameObject *f, int throwflag) } SET_TOP(exit); Py_DECREF(mgr); - res = PyObject_CallFunctionObjArgs(enter, NULL); + res = _PyObject_CallNoArg(enter); Py_DECREF(enter); if (res == NULL) goto error; -- cgit v1.2.1 From 0a023be4188e0fae813a3a468528452c02f553bb Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Fri, 9 Dec 2016 17:12:17 +0100 Subject: Inline PyEval_EvalFrameEx() in callers The PEP 523 modified PyEval_EvalFrameEx(): it's now an indirection to interp->eval_frame(). Inline the call in performance critical code. Leave PyEval_EvalFrame() unchanged, this function is only kept for backward compatibility. --- Python/ceval.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'Python/ceval.c') diff --git a/Python/ceval.c b/Python/ceval.c index c6c6e05650..c4507fb74d 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -4069,7 +4069,7 @@ _PyEval_EvalCodeWithName(PyObject *_co, PyObject *globals, PyObject *locals, return gen; } - retval = PyEval_EvalFrameEx(f,0); + retval = tstate->interp->eval_frame(f, 0); fail: /* Jump here from prelude on failure */ @@ -4822,7 +4822,7 @@ _PyFunction_FastCall(PyCodeObject *co, PyObject **args, Py_ssize_t nargs, Py_INCREF(*args); fastlocals[i] = *args++; } - result = PyEval_EvalFrameEx(f,0); + result = tstate->interp->eval_frame(f,0); ++tstate->recursion_depth; Py_DECREF(f); -- cgit v1.2.1 From 52f6d966c7b6f06c6b140163799cb809373a53c8 Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Fri, 9 Dec 2016 18:51:13 +0100 Subject: Backed out changeset 99c34e47348b The change broke test_gdb. --- Python/ceval.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'Python/ceval.c') diff --git a/Python/ceval.c b/Python/ceval.c index c4507fb74d..c6c6e05650 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -4069,7 +4069,7 @@ _PyEval_EvalCodeWithName(PyObject *_co, PyObject *globals, PyObject *locals, return gen; } - retval = tstate->interp->eval_frame(f, 0); + retval = PyEval_EvalFrameEx(f,0); fail: /* Jump here from prelude on failure */ @@ -4822,7 +4822,7 @@ _PyFunction_FastCall(PyCodeObject *co, PyObject **args, Py_ssize_t nargs, Py_INCREF(*args); fastlocals[i] = *args++; } - result = tstate->interp->eval_frame(f,0); + result = PyEval_EvalFrameEx(f,0); ++tstate->recursion_depth; Py_DECREF(f); -- cgit v1.2.1 From 4f5d39b98cc760dcb7641bb71bdaeb2933b1ee40 Mon Sep 17 00:00:00 2001 From: Yury Selivanov Date: Tue, 13 Dec 2016 19:03:51 -0500 Subject: Issue #26110: Add LOAD_METHOD/CALL_METHOD opcodes. Special thanks to INADA Naoki for pushing the patch through the last mile, Serhiy Storchaka for reviewing the code, and to Victor Stinner for suggesting the idea (originally implemented in the PyPy project). --- Python/ceval.c | 97 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 97 insertions(+) (limited to 'Python/ceval.c') diff --git a/Python/ceval.c b/Python/ceval.c index c6c6e05650..0f1f36e80d 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -30,6 +30,9 @@ #define CHECKEXC 1 /* Double-check exception checking */ #endif +/* Private API for the LOAD_METHOD opcode. */ +extern int _PyObject_GetMethod(PyObject *, PyObject *, PyObject **); + typedef PyObject *(*callproc)(PyObject *, PyObject *, PyObject *); /* Forward declarations */ @@ -3225,6 +3228,100 @@ _PyEval_EvalFrameDefault(PyFrameObject *f, int throwflag) DISPATCH(); } + TARGET(LOAD_METHOD) { + /* Designed to work in tamdem with CALL_METHOD. */ + PyObject *name = GETITEM(names, oparg); + PyObject *obj = TOP(); + PyObject *meth = NULL; + + int meth_found = _PyObject_GetMethod(obj, name, &meth); + + SET_TOP(meth); /* Replace `obj` on top; OK if NULL. */ + if (meth == NULL) { + /* Most likely attribute wasn't found. */ + Py_DECREF(obj); + goto error; + } + + if (meth_found) { + /* The method object is now on top of the stack. + Push `obj` back to the stack, so that the stack + layout would be: + + method | obj | arg1 | ... | argN + */ + PUSH(obj); + } + else { + /* Not a method (but a regular attr, or something + was returned by a descriptor protocol). Push + NULL to the top of the stack, to signal + CALL_METHOD that it's not a method call. + */ + Py_DECREF(obj); + PUSH(NULL); + } + DISPATCH(); + } + + TARGET(CALL_METHOD) { + /* Designed to work in tamdem with LOAD_METHOD. */ + PyObject **sp, *res, *obj; + + sp = stack_pointer; + + obj = PEEK(oparg + 1); + if (obj == NULL) { + /* `obj` is NULL when LOAD_METHOD thinks that it's not + a method call. Swap the NULL and callable. + + Stack layout: + + ... | callable | NULL | arg1 | ... | argN + ^- TOP() + ^- (-oparg) + ^- (-oparg-1) + ^- (-oparg-2) + + after the next line it will be: + + ... | callable | callable | arg1 | ... | argN + ^- TOP() + ^- (-oparg) + ^- (-oparg-1) + ^- (-oparg-2) + + Right side `callable` will be POPed by call_funtion. + Left side `callable` will be POPed manually later + (one of "callbale" refs on the stack is borrowed.) + */ + SET_VALUE(oparg + 1, PEEK(oparg + 2)); + res = call_function(&sp, oparg, NULL); + stack_pointer = sp; + (void)POP(); /* POP the left side callable. */ + } + else { + /* This is a method call. Stack layout: + + ... | method | obj | arg1 | ... | argN + ^- TOP() + ^- (-oparg) + ^- (-oparg-1) + + `obj` and `method` will be POPed by call_function. + We'll be passing `oparg + 1` to call_function, to + make it accept the `obj` as a first argument. + */ + res = call_function(&sp, oparg + 1, NULL); + stack_pointer = sp; + } + + PUSH(res); + if (res == NULL) + goto error; + DISPATCH(); + } + PREDICTED(CALL_FUNCTION); TARGET(CALL_FUNCTION) { PyObject **sp, *res; -- cgit v1.2.1 From f22b56b777f7134b5c4e6c9caa9249e92c4f4fd9 Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Fri, 16 Dec 2016 16:18:57 +0200 Subject: Issue #28959: Added private macro PyDict_GET_SIZE for retrieving the size of dict. --- Python/ceval.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'Python/ceval.c') diff --git a/Python/ceval.c b/Python/ceval.c index 0f1f36e80d..fc11117109 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -5010,7 +5010,7 @@ _PyFunction_FastCallDict(PyObject *func, PyObject **args, Py_ssize_t nargs, assert(kwargs == NULL || PyDict_Check(kwargs)); if (co->co_kwonlyargcount == 0 && - (kwargs == NULL || PyDict_Size(kwargs) == 0) && + (kwargs == NULL || PyDict_GET_SIZE(kwargs) == 0) && co->co_flags == (CO_OPTIMIZED | CO_NEWLOCALS | CO_NOFREE)) { /* Fast paths */ @@ -5028,7 +5028,7 @@ _PyFunction_FastCallDict(PyObject *func, PyObject **args, Py_ssize_t nargs, if (kwargs != NULL) { Py_ssize_t pos, i; - nk = PyDict_Size(kwargs); + nk = PyDict_GET_SIZE(kwargs); kwtuple = PyTuple_New(2 * nk); if (kwtuple == NULL) { -- cgit v1.2.1 From b738df1cd4bcfdf6e01ea459ca559cd64f0bf6f3 Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Fri, 16 Dec 2016 19:19:02 +0200 Subject: Issue #18896: Python function can now have more than 255 parameters. collections.namedtuple() now supports tuples with more than 255 elements. --- Python/ceval.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'Python/ceval.c') diff --git a/Python/ceval.c b/Python/ceval.c index fc11117109..f7ee041864 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -4100,7 +4100,7 @@ _PyEval_EvalCodeWithName(PyObject *_co, PyObject *globals, PyObject *locals, vars into frame. */ for (i = 0; i < PyTuple_GET_SIZE(co->co_cellvars); ++i) { PyObject *c; - int arg; + Py_ssize_t arg; /* Possibly account for the cell variable being an argument. */ if (co->co_cell2arg != NULL && (arg = co->co_cell2arg[i]) != CO_CELL_NOT_AN_ARG) { -- cgit v1.2.1 From febbf471bd550945621b470089b6c3dc872ae894 Mon Sep 17 00:00:00 2001 From: INADA Naoki Date: Sat, 24 Dec 2016 20:19:08 +0900 Subject: Issue #29049: Call _PyObject_GC_TRACK() lazily when calling Python function. Calling function is up to 5% faster. --- Python/ceval.c | 31 +++++++++++++++++++++---------- 1 file changed, 21 insertions(+), 10 deletions(-) (limited to 'Python/ceval.c') diff --git a/Python/ceval.c b/Python/ceval.c index f7ee041864..e48586dec2 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -3931,7 +3931,7 @@ _PyEval_EvalCodeWithName(PyObject *_co, PyObject *globals, PyObject *locals, /* Create the frame */ tstate = PyThreadState_GET(); assert(tstate != NULL); - f = PyFrame_New(tstate, co, globals, locals); + f = _PyFrame_New_NoTrack(tstate, co, globals, locals); if (f == NULL) { return NULL; } @@ -4176,9 +4176,15 @@ fail: /* Jump here from prelude on failure */ so recursion_depth must be boosted for the duration. */ assert(tstate != NULL); - ++tstate->recursion_depth; - Py_DECREF(f); - --tstate->recursion_depth; + if (Py_REFCNT(f) > 1) { + Py_DECREF(f); + _PyObject_GC_TRACK(f); + } + else { + ++tstate->recursion_depth; + Py_DECREF(f); + --tstate->recursion_depth; + } return retval; } @@ -4904,11 +4910,11 @@ _PyFunction_FastCall(PyCodeObject *co, PyObject **args, Py_ssize_t nargs, assert(globals != NULL); /* XXX Perhaps we should create a specialized - PyFrame_New() that doesn't take locals, but does + _PyFrame_New_NoTrack() that doesn't take locals, but does take builtins without sanity checking them. */ assert(tstate != NULL); - f = PyFrame_New(tstate, co, globals, NULL); + f = _PyFrame_New_NoTrack(tstate, co, globals, NULL); if (f == NULL) { return NULL; } @@ -4921,10 +4927,15 @@ _PyFunction_FastCall(PyCodeObject *co, PyObject **args, Py_ssize_t nargs, } result = PyEval_EvalFrameEx(f,0); - ++tstate->recursion_depth; - Py_DECREF(f); - --tstate->recursion_depth; - + if (Py_REFCNT(f) > 1) { + Py_DECREF(f); + _PyObject_GC_TRACK(f); + } + else { + ++tstate->recursion_depth; + Py_DECREF(f); + --tstate->recursion_depth; + } return result; } -- cgit v1.2.1 From a3f0a4ccc474eec5fe8c0d79856bc3052accfe98 Mon Sep 17 00:00:00 2001 From: INADA Naoki Date: Mon, 26 Dec 2016 18:01:46 +0900 Subject: Issue #29049: Fix refleak introduced by f5eb0c4f5d37. --- Python/ceval.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'Python/ceval.c') diff --git a/Python/ceval.c b/Python/ceval.c index e48586dec2..08fc27f246 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -4152,8 +4152,11 @@ _PyEval_EvalCodeWithName(PyObject *_co, PyObject *globals, PyObject *locals, } else { gen = PyGen_NewWithQualName(f, name, qualname); } - if (gen == NULL) + if (gen == NULL) { + Py_DECREF(f); return NULL; + } + _PyObject_GC_TRACK(f); if (is_coro && coro_wrapper != NULL) { PyObject *wrapped; -- cgit v1.2.1 From ea06a36838892fd7d6fd71cb0a4822ac26b065fe Mon Sep 17 00:00:00 2001 From: INADA Naoki Date: Mon, 26 Dec 2016 18:52:46 +0900 Subject: Issue #29049: Remove unnecessary Py_DECREF --- Python/ceval.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'Python/ceval.c') diff --git a/Python/ceval.c b/Python/ceval.c index 08fc27f246..5519ac7555 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -4153,9 +4153,9 @@ _PyEval_EvalCodeWithName(PyObject *_co, PyObject *globals, PyObject *locals, gen = PyGen_NewWithQualName(f, name, qualname); } if (gen == NULL) { - Py_DECREF(f); return NULL; } + _PyObject_GC_TRACK(f); if (is_coro && coro_wrapper != NULL) { -- cgit v1.2.1 From 9269fbdf7669f0a953c292f77afc6d98ccfb4def Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Tue, 3 Jan 2017 02:01:42 +0100 Subject: Optimize _PyFunction_FastCallDict() when kwargs is {} Issue #28839: Optimize _PyFunction_FastCallDict() when kwargs is an empty dictionary, avoid the creation of an useless empty tuple. --- Python/ceval.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) (limited to 'Python/ceval.c') diff --git a/Python/ceval.c b/Python/ceval.c index 5519ac7555..cd95642923 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -5040,9 +5040,9 @@ _PyFunction_FastCallDict(PyObject *func, PyObject **args, Py_ssize_t nargs, } } - if (kwargs != NULL) { + nk = (kwargs != NULL) ? PyDict_GET_SIZE(kwargs) : 0; + if (nk != 0) { Py_ssize_t pos, i; - nk = PyDict_GET_SIZE(kwargs); kwtuple = PyTuple_New(2 * nk); if (kwtuple == NULL) { @@ -5052,6 +5052,9 @@ _PyFunction_FastCallDict(PyObject *func, PyObject **args, Py_ssize_t nargs, k = &PyTuple_GET_ITEM(kwtuple, 0); pos = i = 0; while (PyDict_Next(kwargs, &pos, &k[i], &k[i+1])) { + /* We must hold strong references because keyword arguments can be + indirectly modified while the function is called: + see issue #2016 and test_extcall */ Py_INCREF(k[i]); Py_INCREF(k[i+1]); i += 2; @@ -5061,7 +5064,6 @@ _PyFunction_FastCallDict(PyObject *func, PyObject **args, Py_ssize_t nargs, else { kwtuple = NULL; k = NULL; - nk = 0; } kwdefs = PyFunction_GET_KW_DEFAULTS(func); -- cgit v1.2.1 From 7ee4a773664fa60b93a008db32bb0e8609175215 Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Wed, 11 Jan 2017 00:54:57 +0100 Subject: Inline call_function() Issue #29227: Inline call_function() into _PyEval_EvalFrameDefault() using Py_LOCAL_INLINE to reduce the stack consumption. It reduces the stack consumption, bytes per call, before => after: test_python_call: 1152 => 1040 (-112 B) test_python_getitem: 1008 => 976 (-32 B) test_python_iterator: 1232 => 1120 (-112 B) => total: 3392 => 3136 (- 256 B) --- Python/ceval.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'Python/ceval.c') diff --git a/Python/ceval.c b/Python/ceval.c index cd95642923..f86f6aafdc 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -36,7 +36,7 @@ extern int _PyObject_GetMethod(PyObject *, PyObject *, PyObject **); typedef PyObject *(*callproc)(PyObject *, PyObject *, PyObject *); /* Forward declarations */ -static PyObject * call_function(PyObject ***, Py_ssize_t, PyObject *); +Py_LOCAL_INLINE(PyObject *) call_function(PyObject ***, Py_ssize_t, PyObject *); static PyObject * fast_function(PyObject *, PyObject **, Py_ssize_t, PyObject *); static PyObject * do_call_core(PyObject *, PyObject *, PyObject *); @@ -4829,7 +4829,9 @@ if (tstate->use_tracing && tstate->c_profilefunc) { \ x = call; \ } -static PyObject* _Py_HOT_FUNCTION +/* Issue #29227: Inline call_function() into _PyEval_EvalFrameDefault() + to reduce the stack consumption. */ +Py_LOCAL_INLINE(PyObject *) _Py_HOT_FUNCTION call_function(PyObject ***pp_stack, Py_ssize_t oparg, PyObject *kwnames) { PyObject **pfunc = (*pp_stack) - oparg - 1; -- cgit v1.2.1 From 36e18bf2fa1a07b5f7b67f55c038cdb90a17dc19 Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Wed, 11 Jan 2017 02:12:06 +0100 Subject: _PyEval_EvalCodeWithName(): remove redundant check Replace the runtime check with an assertion (just in case). --- Python/ceval.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'Python/ceval.c') diff --git a/Python/ceval.c b/Python/ceval.c index f86f6aafdc..b970ece4e9 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -4017,7 +4017,8 @@ _PyEval_EvalCodeWithName(PyObject *_co, PyObject *globals, PyObject *locals, } } - if (j >= total_args && kwdict == NULL) { + assert(j >= total_args); + if (kwdict == NULL) { PyErr_Format(PyExc_TypeError, "%U() got an unexpected keyword argument '%S'", co->co_name, keyword); -- cgit v1.2.1 From fb133ca2ee4decf7f9a30ad020564617783403ce Mon Sep 17 00:00:00 2001 From: INADA Naoki Date: Mon, 16 Jan 2017 17:23:30 +0900 Subject: Issue #26110: Add document for LOAD_METHOD and CALL_METHOD opcode. Changed stack layout bit for "easy to explain." --- Python/ceval.c | 72 ++++++++++++++++++++++++++-------------------------------- 1 file changed, 32 insertions(+), 40 deletions(-) (limited to 'Python/ceval.c') diff --git a/Python/ceval.c b/Python/ceval.c index b970ece4e9..1b8cdfc663 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -3236,81 +3236,73 @@ _PyEval_EvalFrameDefault(PyFrameObject *f, int throwflag) int meth_found = _PyObject_GetMethod(obj, name, &meth); - SET_TOP(meth); /* Replace `obj` on top; OK if NULL. */ if (meth == NULL) { /* Most likely attribute wasn't found. */ - Py_DECREF(obj); goto error; } if (meth_found) { - /* The method object is now on top of the stack. - Push `obj` back to the stack, so that the stack - layout would be: - - method | obj | arg1 | ... | argN - */ - PUSH(obj); + /* We can bypass temporary bound method object. + meth is unbound method and obj is self. + + meth | self | arg1 | ... | argN + */ + SET_TOP(meth); + PUSH(obj); // self } else { - /* Not a method (but a regular attr, or something - was returned by a descriptor protocol). Push - NULL to the top of the stack, to signal + /* meth is not an unbound method (but a regular attr, or + something was returned by a descriptor protocol). Set + the second element of the stack to NULL, to signal CALL_METHOD that it's not a method call. + + NULL | meth | arg1 | ... | argN */ + SET_TOP(NULL); Py_DECREF(obj); - PUSH(NULL); + PUSH(meth); } DISPATCH(); } TARGET(CALL_METHOD) { /* Designed to work in tamdem with LOAD_METHOD. */ - PyObject **sp, *res, *obj; + PyObject **sp, *res, *meth; sp = stack_pointer; - obj = PEEK(oparg + 1); - if (obj == NULL) { - /* `obj` is NULL when LOAD_METHOD thinks that it's not - a method call. Swap the NULL and callable. + meth = PEEK(oparg + 2); + if (meth == NULL) { + /* `meth` is NULL when LOAD_METHOD thinks that it's not + a method call. Stack layout: - ... | callable | NULL | arg1 | ... | argN - ^- TOP() - ^- (-oparg) - ^- (-oparg-1) - ^- (-oparg-2) - - after the next line it will be: - - ... | callable | callable | arg1 | ... | argN - ^- TOP() - ^- (-oparg) - ^- (-oparg-1) - ^- (-oparg-2) + ... | NULL | callable | arg1 | ... | argN + ^- TOP() + ^- (-oparg) + ^- (-oparg-1) + ^- (-oparg-2) - Right side `callable` will be POPed by call_funtion. - Left side `callable` will be POPed manually later - (one of "callbale" refs on the stack is borrowed.) + `callable` will be POPed by call_funtion. + NULL will will be POPed manually later. */ - SET_VALUE(oparg + 1, PEEK(oparg + 2)); res = call_function(&sp, oparg, NULL); stack_pointer = sp; - (void)POP(); /* POP the left side callable. */ + (void)POP(); /* POP the NULL. */ } else { /* This is a method call. Stack layout: - ... | method | obj | arg1 | ... | argN + ... | method | self | arg1 | ... | argN ^- TOP() ^- (-oparg) - ^- (-oparg-1) + ^- (-oparg-1) + ^- (-oparg-2) - `obj` and `method` will be POPed by call_function. + `self` and `method` will be POPed by call_function. We'll be passing `oparg + 1` to call_function, to - make it accept the `obj` as a first argument. + make it accept the `self` as a first argument. */ res = call_function(&sp, oparg + 1, NULL); stack_pointer = sp; -- cgit v1.2.1 From 4a6bc8201521488a56ced3d3825e21915f681fae Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Wed, 18 Jan 2017 14:12:51 +0100 Subject: Rephrase !PyErr_Occurred() comment: may=>can Issue #29259. --- Python/ceval.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'Python/ceval.c') diff --git a/Python/ceval.c b/Python/ceval.c index 1b8cdfc663..87384709c9 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -1046,7 +1046,7 @@ _PyEval_EvalFrameDefault(PyFrameObject *f, int throwflag) #ifdef Py_DEBUG /* PyEval_EvalFrameEx() must not be called with an exception set, - because it may clear it (directly or indirectly) and so the + because it can clear it (directly or indirectly) and so the caller loses its exception */ assert(!PyErr_Occurred()); #endif @@ -3244,7 +3244,7 @@ _PyEval_EvalFrameDefault(PyFrameObject *f, int throwflag) if (meth_found) { /* We can bypass temporary bound method object. meth is unbound method and obj is self. - + meth | self | arg1 | ... | argN */ SET_TOP(meth); -- cgit v1.2.1 From d9d677581682d1b326293ea2b62beaefdb34084f Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Wed, 1 Feb 2017 17:04:52 +0100 Subject: Document that _PyFunction_FastCallDict() must copy kwargs Issue #29318: Caller and callee functions must not share the dictionary: kwargs must be copied. --- Python/ceval.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'Python/ceval.c') diff --git a/Python/ceval.c b/Python/ceval.c index 87384709c9..298ad55b81 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -5039,6 +5039,8 @@ _PyFunction_FastCallDict(PyObject *func, PyObject **args, Py_ssize_t nargs, if (nk != 0) { Py_ssize_t pos, i; + /* Issue #29318: Caller and callee functions must not share the + dictionary: kwargs must be copied. */ kwtuple = PyTuple_New(2 * nk); if (kwtuple == NULL) { return NULL; -- cgit v1.2.1 From 99c092a531abf57a8f5c37356dc6971cdfed497a Mon Sep 17 00:00:00 2001 From: INADA Naoki Date: Fri, 3 Feb 2017 07:43:03 +0900 Subject: Issue #29263: LOAD_METHOD support for C methods Calling builtin method is at most 10% faster. --- Python/ceval.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) (limited to 'Python/ceval.c') diff --git a/Python/ceval.c b/Python/ceval.c index 298ad55b81..58007793bb 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -4832,17 +4832,19 @@ call_function(PyObject ***pp_stack, Py_ssize_t oparg, PyObject *kwnames) PyObject *x, *w; Py_ssize_t nkwargs = (kwnames == NULL) ? 0 : PyTuple_GET_SIZE(kwnames); Py_ssize_t nargs = oparg - nkwargs; - PyObject **stack; + PyObject **stack = (*pp_stack) - nargs - nkwargs; /* Always dispatch PyCFunction first, because these are presumed to be the most frequent callable object. */ if (PyCFunction_Check(func)) { PyThreadState *tstate = PyThreadState_GET(); - - stack = (*pp_stack) - nargs - nkwargs; C_TRACE(x, _PyCFunction_FastCallKeywords(func, stack, nargs, kwnames)); } + else if (Py_TYPE(func) == &PyMethodDescr_Type) { + PyThreadState *tstate = PyThreadState_GET(); + C_TRACE(x, _PyMethodDescr_FastCallKeywords(func, stack, nargs, kwnames)); + } else { if (PyMethod_Check(func) && PyMethod_GET_SELF(func) != NULL) { /* Optimize access to bound methods. Reuse the Python stack @@ -4856,20 +4858,18 @@ call_function(PyObject ***pp_stack, Py_ssize_t oparg, PyObject *kwnames) Py_INCREF(func); Py_SETREF(*pfunc, self); nargs++; + stack--; } else { Py_INCREF(func); } - stack = (*pp_stack) - nargs - nkwargs; - if (PyFunction_Check(func)) { x = fast_function(func, stack, nargs, kwnames); } else { x = _PyObject_FastCallKeywords(func, stack, nargs, kwnames); } - Py_DECREF(func); } -- cgit v1.2.1