diff --git a/Makefile.pre.in b/Makefile.pre.in index 4071a2a..e729ffb 100644 --- a/Makefile.pre.in +++ b/Makefile.pre.in @@ -879,7 +879,9 @@ Objects/setobject.o: $(srcdir)/Objects/stringlib/eq.h $(OPCODETARGETS_H): $(OPCODETARGETGEN_FILES) $(OPCODETARGETGEN) $(OPCODETARGETS_H) -Python/ceval.o: $(OPCODETARGETS_H) $(srcdir)/Python/ceval_gil.h +Python/ceval.o: $(OPCODETARGETS_H) $(srcdir)/Python/ceval_gil.h $(srcdir)/Python/wordcode_helpers.h +Python/compile.o: $(srcdir)/Python/wordcode_helpers.h +Python/peephole.o: $(srcdir)/Python/wordcode_helpers.h Python/frozen.o: Python/importlib.h Python/importlib_external.h diff --git a/Python/ceval.c b/Python/ceval.c index 20e93cc..3096ad9 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -17,6 +17,7 @@ #include "opcode.h" #include "setobject.h" #include "structmember.h" +#include "wordcode_helpers.h" #include @@ -136,7 +137,8 @@ static int call_trace_protected(Py_tracefunc, PyObject *, static void call_exc_trace(Py_tracefunc, PyObject *, PyThreadState *, PyFrameObject *); static int maybe_call_line_trace(Py_tracefunc, PyObject *, - PyThreadState *, PyFrameObject *, int *, int *, int *); + PyThreadState *, PyFrameObject *, + int *, int *, int *, int, int); static PyObject * cmp_outcome(int, PyObject *, PyObject *); static PyObject * import_from(PyObject *, PyObject *); @@ -1307,14 +1309,24 @@ PyEval_EvalFrameEx(PyFrameObject *f, int throwflag) if (_Py_TracingPossible && tstate->c_tracefunc != NULL && !tstate->tracing) { int err; + int lasti; + if (OPCODE(*next_instr) == FOR_ITER) { + /* Trace FOR_ITER as if we're the corresponding FOR_BEGIN, + as tracer expects loop logic to precede loop body */ + lasti = (int)get_arg((unsigned char*)next_instr, 0) - 2; + } + else { + lasti = f->f_lasti; + } /* see maybe_call_line_trace for expository comments */ f->f_stacktop = stack_pointer; err = maybe_call_line_trace(tstate->c_tracefunc, - tstate->c_traceobj, - tstate, f, - &instr_lb, &instr_ub, &instr_prev); + tstate->c_traceobj, tstate, f, + &instr_lb, &instr_ub, &instr_prev, + f->f_lasti, lasti); + /* Reload possibly changed frame fields */ JUMPTO(f->f_lasti); if (f->f_stacktop != NULL) { @@ -1670,7 +1682,7 @@ PyEval_EvalFrameEx(PyFrameObject *f, int throwflag) Py_DECREF(v); if (err != 0) goto error; - PREDICT(JUMP_ABSOLUTE); + PREDICT(FOR_ITER); DISPATCH(); } @@ -1682,7 +1694,7 @@ PyEval_EvalFrameEx(PyFrameObject *f, int throwflag) Py_DECREF(v); if (err != 0) goto error; - PREDICT(JUMP_ABSOLUTE); + PREDICT(FOR_ITER); DISPATCH(); } @@ -2704,7 +2716,7 @@ PyEval_EvalFrameEx(PyFrameObject *f, int throwflag) Py_DECREF(key); if (err != 0) goto error; - PREDICT(JUMP_ABSOLUTE); + PREDICT(FOR_ITER); DISPATCH(); } @@ -2920,7 +2932,6 @@ PyEval_EvalFrameEx(PyFrameObject *f, int throwflag) DISPATCH(); } - PREDICTED(JUMP_ABSOLUTE); TARGET(JUMP_ABSOLUTE) { JUMPTO(oparg); #if FAST_LOOPS @@ -2999,6 +3010,7 @@ PyEval_EvalFrameEx(PyFrameObject *f, int throwflag) DISPATCH(); } + PREDICTED(FOR_ITER) TARGET(FOR_ITER) { /* before: [iter]; after: [iter, iter()] *or* [] */ PyObject *iter = TOP(); @@ -4445,7 +4457,7 @@ _PyEval_CallTracing(PyObject *func, PyObject *args) static int maybe_call_line_trace(Py_tracefunc func, PyObject *obj, PyThreadState *tstate, PyFrameObject *frame, - int *instr_lb, int *instr_ub, int *instr_prev) + int *instr_lb, int *instr_ub, int *instr_prev, int lasti, int nlasti) { int result = 0; int line = frame->f_lineno; @@ -4453,9 +4465,10 @@ maybe_call_line_trace(Py_tracefunc func, PyObject *obj, /* If the last instruction executed isn't in the current instruction window, reset the window. */ - if (frame->f_lasti < *instr_lb || frame->f_lasti >= *instr_ub) { +redo: + if (lasti < *instr_lb || lasti >= *instr_ub) { PyAddrPair bounds; - line = _PyCode_CheckLineNumber(frame->f_code, frame->f_lasti, + line = _PyCode_CheckLineNumber(frame->f_code, lasti, &bounds); *instr_lb = bounds.ap_lower; *instr_ub = bounds.ap_upper; @@ -4463,11 +4476,22 @@ maybe_call_line_trace(Py_tracefunc func, PyObject *obj, /* If the last instruction falls at the start of a line or if it represents a jump backwards, update the frame's line number and call the trace function. */ - if (frame->f_lasti == *instr_lb || frame->f_lasti < *instr_prev) { + if (lasti == *instr_lb || lasti < *instr_prev) { frame->f_lineno = line; result = call_trace(func, obj, tstate, frame, PyTrace_LINE, Py_None); } - *instr_prev = frame->f_lasti; + + if (!result && lasti != nlasti) { + /* FOR_ITER may generate 2 line traces if the body is 'pass' + We want to generate a trace as if the end of the loop is a mere + JUMP_ABSOLUTE so we manually trace FOR_ITER as if FOR_BEGIN is + being executed each loop in lieu of there being no better instruction + at the beginning to designate loop iteration logic */ + lasti = nlasti; + goto redo; + } + + *instr_prev = lasti; return result; } diff --git a/Python/peephole.c b/Python/peephole.c index 5795c62..94f5f47 100644 --- a/Python/peephole.c +++ b/Python/peephole.c @@ -104,25 +104,6 @@ find_op(unsigned char *codestr, Py_ssize_t i) } /* Given the index of the effective opcode, - scan back to construct the oparg with EXTENDED_ARG */ -static unsigned int -get_arg(unsigned char *codestr, Py_ssize_t i) -{ - unsigned int oparg = codestr[i+1]; - assert((i&1) == 0); - if (i >= 2 && codestr[i-2] == EXTENDED_ARG) { - oparg |= codestr[i-1] << 8; - if (i >= 4 && codestr[i-4] == EXTENDED_ARG) { - oparg |= codestr[i-3] << 16; - if (i >= 6 && codestr[i-6] == EXTENDED_ARG) { - oparg |= codestr[i-5] << 24; - } - } - } - return oparg; -} - -/* Given the index of the effective opcode, attempt to replace the argument, taking into account EXTENDED_ARG. Returns -1 on failure, or the new op index on success */ static Py_ssize_t @@ -539,7 +520,7 @@ PyCode_Optimize(PyObject *code, PyObject* consts, PyObject *names, ((opcode == BUILD_LIST || opcode == BUILD_SET) && ((nextop==COMPARE_OP && (codestr[nexti+1]==6 || - codestr[nexti+1]==7)) + codestr[nexti+1]==7)) || nextop == FOR_BEGIN) && ISBASICBLOCK(blocks, h, i + 2))) { h = fold_tuple_on_constants(codestr, h, i+2, opcode, consts, CONST_STACK_LASTN(j), j); diff --git a/Python/wordcode_helpers.h b/Python/wordcode_helpers.h index b61ba33..f6126e5 100644 --- a/Python/wordcode_helpers.h +++ b/Python/wordcode_helpers.h @@ -3,7 +3,7 @@ */ /* Minimum number of bytes necessary to encode instruction with EXTENDED_ARGs */ -static int +Py_LOCAL_INLINE(int) instrsize(unsigned int oparg) { return oparg <= 0xff ? 2 : @@ -14,7 +14,7 @@ instrsize(unsigned int oparg) /* Spits out op/oparg pair using ilen bytes. codestr should be pointed at the desired location of the first EXTENDED_ARG */ -static void +Py_LOCAL_INLINE(void) write_op_arg(unsigned char *codestr, unsigned char opcode, unsigned int oparg, int ilen) { @@ -36,3 +36,23 @@ write_op_arg(unsigned char *codestr, unsigned char opcode, assert(0); } } + +/* Given the index of the effective opcode, + scan back to construct the oparg with EXTENDED_ARG */ +Py_LOCAL_INLINE(unsigned int) +get_arg(unsigned char *codestr, Py_ssize_t i) +{ + unsigned int oparg = codestr[i+1]; + assert((i&1) == 0); + if (i >= 2 && codestr[i-2] == EXTENDED_ARG) { + oparg |= codestr[i-1] << 8; + if (i >= 4 && codestr[i-4] == EXTENDED_ARG) { + oparg |= codestr[i-3] << 16; + if (i >= 6 && codestr[i-6] == EXTENDED_ARG) { + oparg |= codestr[i-5] << 24; + } + } + } + return oparg; +} +