summaryrefslogtreecommitdiff
path: root/Python/ceval.c
diff options
context:
space:
mode:
Diffstat (limited to 'Python/ceval.c')
-rw-r--r--Python/ceval.c309
1 files changed, 177 insertions, 132 deletions
diff --git a/Python/ceval.c b/Python/ceval.c
index d5172b9631..5519ac7555 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)) {
@@ -3879,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;
}
@@ -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) {
@@ -4102,8 +4152,11 @@ _PyEval_EvalCodeWithName(PyObject *_co, PyObject *globals, PyObject *locals,
} else {
gen = PyGen_NewWithQualName(f, name, qualname);
}
- if (gen == NULL)
+ if (gen == NULL) {
return NULL;
+ }
+
+ _PyObject_GC_TRACK(f);
if (is_coro && coro_wrapper != NULL) {
PyObject *wrapped;
@@ -4126,9 +4179,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;
}
@@ -4245,7 +4304,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 +4329,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 +4358,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 +4743,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 +4754,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 +4769,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 +4829,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 +4845,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 +4887,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 +4901,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,14 +4911,13 @@ _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
+ _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;
}
@@ -4869,10 +4930,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;
}
@@ -4895,9 +4961,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 +5023,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 +5042,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 +5090,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 +5388,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 +5475,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 +5489,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 +5506,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.