diff options
Diffstat (limited to 'Python/ceval.c')
-rw-r--r-- | Python/ceval.c | 273 |
1 files changed, 152 insertions, 121 deletions
diff --git a/Python/ceval.c b/Python/ceval.c index d5172b9631..f7ee041864 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 */ @@ -83,63 +86,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 @@ -718,7 +664,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 @@ -1424,6 +1370,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); @@ -1538,7 +1490,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); @@ -2400,8 +2352,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); @@ -2798,7 +2752,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); @@ -3111,7 +3065,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; @@ -3145,7 +3099,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; @@ -3184,8 +3138,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(); @@ -3229,8 +3187,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; @@ -3267,10 +3228,103 @@ _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; - PCALL(PCALL_ALL); sp = stack_pointer; res = call_function(&sp, oparg, NULL); stack_pointer = sp; @@ -3286,7 +3340,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; @@ -3301,7 +3354,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)) { @@ -4048,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) { @@ -4091,8 +4143,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) { @@ -4245,7 +4295,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)) { @@ -4270,10 +4320,13 @@ do_raise(PyObject *exc, PyObject *cause) goto raise_error; } + assert(type != NULL); + assert(value != NULL); + 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); @@ -4296,8 +4349,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: @@ -4681,7 +4734,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 @@ -4691,7 +4745,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)) { @@ -4706,7 +4760,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 * @@ -4766,7 +4820,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; @@ -4782,17 +4836,17 @@ 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)); } 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); - PCALL(PCALL_METHOD); - PCALL(PCALL_BOUND_METHOD); Py_INCREF(self); func = PyMethod_GET_FUNCTION(func); Py_INCREF(func); @@ -4824,7 +4878,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; @@ -4839,7 +4892,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) { @@ -4849,7 +4902,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 @@ -4895,9 +4947,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)) { @@ -4960,11 +5009,8 @@ _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) && + (kwargs == NULL || PyDict_GET_SIZE(kwargs) == 0) && co->co_flags == (CO_OPTIMIZED | CO_NEWLOCALS | CO_NOFREE)) { /* Fast paths */ @@ -4982,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) { @@ -5030,23 +5076,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(); @@ -5345,8 +5374,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: @@ -5430,8 +5461,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); @@ -5444,8 +5475,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); @@ -5461,7 +5492,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. |