diff options
author | Mark Shannon <mark@hotpy.org> | 2021-04-08 10:22:55 (GMT) |
---|---|---|
committer | GitHub <noreply@github.com> | 2021-04-08 10:22:55 (GMT) |
commit | 28d28e053db6b69d91c2dfd579207cd8ccbc39e7 (patch) | |
tree | bc37ffc6640173d75b449b6dcb11c2bceae6e4a1 | |
parent | b98eba5bc2ffbe7a0ed49d540ebc4f756ae61985 (diff) | |
download | cpython-28d28e053db6b69d91c2dfd579207cd8ccbc39e7.zip cpython-28d28e053db6b69d91c2dfd579207cd8ccbc39e7.tar.gz cpython-28d28e053db6b69d91c2dfd579207cd8ccbc39e7.tar.bz2 |
bpo-43760: Streamline dispatch sequence for machines without computed gotos. (GH-25244)
* Do fetch and decode at end of opcode then jump directly to switch.
Should allow compilers that don't support computed-gotos, specifically MSVC,
to generate better code.
-rw-r--r-- | Python/ceval.c | 113 |
1 files changed, 52 insertions, 61 deletions
diff --git a/Python/ceval.c b/Python/ceval.c index eda9939..ea31179 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 @@ -1282,37 +1299,22 @@ eval_frame_handle_pending(PyThreadState *tstate) #endif #if USE_COMPUTED_GOTOS -#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 TARGET(op) op: TARGET_##op +#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)) { \ @@ -1598,14 +1600,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 +1614,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 +1748,10 @@ main_loop: 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 +1768,15 @@ main_loop: 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); @@ -1805,23 +1798,13 @@ main_loop: JUMPTO(f->f_lasti); stack_pointer = f->f_valuestack+f->f_stackdepth; f->f_stackdepth = -1; - if (err) + 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 +1820,20 @@ main_loop: } #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 +5419,6 @@ Error: return 0; } - #ifdef LLTRACE static int prtrace(PyThreadState *tstate, PyObject *v, const char *str) |