From 82d2cd0c050e918a2099129a1efbe66bbda9384b Mon Sep 17 00:00:00 2001 From: Mark Shannon Date: Fri, 26 Mar 2021 12:26:01 +0000 Subject: [PATCH] Streamline dispatch sequence for machines without computed gotos. * Do fetch and decode at end of opocde then jump directly to switch. Should allow compilers that don't support computed-gotos, specifically MSVC, to generate better code. --- Python/ceval.c | 115 +++++++++++++++++++++++-------------------------- 1 file changed, 54 insertions(+), 61 deletions(-) diff --git a/Python/ceval.c b/Python/ceval.c index eda993927093c4..1c59641dc7e3a8 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -1264,6 +1264,23 @@ eval_frame_handle_pending(PyThreadState *tstate) -fno-crossjumping). */ +/* Use macros rather than inline functions, to make it as clear as possible + * to the C compiler that the tracing check is a simple test then branch. + * We want to be sure that the compiler knows this before it generates + * the CFG. + */ +#ifdef LLTRACE +#define OR_LLTRACE || lltrace +#else +#define OR_LLTRACE +#endif + +#ifdef WITH_DTRACE +#define OR_DTRACE_LINE || PyDTrace_LINE_ENABLED() +#else +#define OR_DTRACE_LINE +#endif + #ifdef DYNAMIC_EXECUTION_PROFILE #undef USE_COMPUTED_GOTOS #define USE_COMPUTED_GOTOS 0 @@ -1285,34 +1302,21 @@ eval_frame_handle_pending(PyThreadState *tstate) #define TARGET(op) \ op: \ TARGET_##op - -#ifdef LLTRACE -#define DISPATCH() \ - { \ - if (!lltrace && !_Py_TracingPossible(ceval2) && !PyDTrace_LINE_ENABLED()) { \ - f->f_lasti = INSTR_OFFSET(); \ - NEXTOPARG(); \ - goto *opcode_targets[opcode]; \ - } \ - goto fast_next_opcode; \ - } +#define DISPATCH_GOTO goto *opcode_targets[opcode]; #else +#define TARGET(op) op +#define DISPATCH_GOTO goto dispatch_opcode; +#endif + #define DISPATCH() \ { \ - if (!_Py_TracingPossible(ceval2) && !PyDTrace_LINE_ENABLED()) { \ - f->f_lasti = INSTR_OFFSET(); \ - NEXTOPARG(); \ - goto *opcode_targets[opcode]; \ + if (_Py_TracingPossible(ceval2) OR_DTRACE_LINE OR_LLTRACE) { \ + goto tracing_dispatch; \ } \ - goto fast_next_opcode; \ + f->f_lasti = INSTR_OFFSET(); \ + NEXTOPARG(); \ + DISPATCH_GOTO \ } -#endif - -#else -#define TARGET(op) op -#define DISPATCH() goto fast_next_opcode - -#endif #define CHECK_EVAL_BREAKER() \ if (_Py_atomic_load_relaxed(eval_breaker)) { \ @@ -1590,7 +1594,7 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, PyFrameObject *f, int throwflag) #endif PyObject **stack_pointer; /* Next free slot in value stack */ const _Py_CODEUNIT *next_instr; - int opcode; /* Current opcode */ + uint8_t opcode; /* Current opcode */ int oparg; /* Current opcode argument, if any */ PyObject **fastlocals, **freevars; PyObject *retval = NULL; /* Return value */ @@ -1598,14 +1602,6 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, PyFrameObject *f, int throwflag) _Py_atomic_int * const eval_breaker = &ceval2->eval_breaker; PyCodeObject *co; - /* when tracing we set things up so that - - not (instr_lb <= current_bytecode_offset < instr_ub) - - is true when the line being executed has changed. The - initial values are such as to make this false the first - time it is tested. */ - const _Py_CODEUNIT *first_instr; PyObject *names; PyObject *consts; @@ -1620,7 +1616,7 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, PyFrameObject *f, int throwflag) } PyTraceInfo trace_info; - /* Mark trace_info as initialized */ + /* Mark trace_info as uninitialized */ trace_info.code = NULL; /* push frame */ @@ -1754,10 +1750,10 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, PyFrameObject *f, int throwflag) if (_Py_atomic_load_relaxed(eval_breaker)) { opcode = _Py_OPCODE(*next_instr); - if (opcode == SETUP_FINALLY || - opcode == SETUP_WITH || - opcode == BEFORE_ASYNC_WITH || - opcode == YIELD_FROM) { + if (opcode != SETUP_FINALLY && + opcode != SETUP_WITH && + opcode != BEFORE_ASYNC_WITH && + opcode != YIELD_FROM) { /* Few cases where we skip running signal handlers and other pending calls: - If we're about to enter the 'with:'. It will prevent @@ -1774,16 +1770,15 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, PyFrameObject *f, int throwflag) running the signal handler and raising KeyboardInterrupt (see bpo-30039). */ - goto fast_next_opcode; - } - - if (eval_frame_handle_pending(tstate) != 0) { - goto error; - } + if (eval_frame_handle_pending(tstate) != 0) { + goto error; + } + } } - fast_next_opcode: + tracing_dispatch: f->f_lasti = INSTR_OFFSET(); + NEXTOPARG(); if (PyDTrace_LINE_ENABLED()) maybe_dtrace_line(f, &trace_info); @@ -1801,27 +1796,17 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, PyFrameObject *f, int throwflag) tstate->c_traceobj, tstate, f, &trace_info); + if (err) { + /* trace function raised an exception */ + goto error; + } /* Reload possibly changed frame fields */ JUMPTO(f->f_lasti); stack_pointer = f->f_valuestack+f->f_stackdepth; f->f_stackdepth = -1; - if (err) - /* trace function raised an exception */ - goto error; + NEXTOPARG(); } - /* Extract opcode and argument */ - - NEXTOPARG(); - dispatch_opcode: -#ifdef DYNAMIC_EXECUTION_PROFILE -#ifdef DXPAIRS - dxpairs[lastopcode][opcode]++; - lastopcode = opcode; -#endif - dxp[opcode]++; -#endif - #ifdef LLTRACE /* Instruction tracing */ @@ -1837,11 +1822,20 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, PyFrameObject *f, int throwflag) } #endif + dispatch_opcode: +#ifdef DYNAMIC_EXECUTION_PROFILE +#ifdef DXPAIRS + dxpairs[lastopcode][opcode]++; + lastopcode = opcode; +#endif + dxp[opcode]++; +#endif + switch (opcode) { /* BEWARE! It is essential that any operation that fails must goto error - and that all operation that succeed call [FAST_]DISPATCH() ! */ + and that all operation that succeed call DISPATCH() ! */ case TARGET(NOP): { DISPATCH(); @@ -5427,7 +5421,6 @@ unpack_iterable(PyThreadState *tstate, PyObject *v, return 0; } - #ifdef LLTRACE static int prtrace(PyThreadState *tstate, PyObject *v, const char *str)