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.
This commit is contained in:
Mark Shannon 2021-04-08 11:22:55 +01:00 committed by GitHub
parent b98eba5bc2
commit 28d28e053d
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23

View file

@ -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,38 +1299,23 @@ 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; \
}
#else
#define DISPATCH() \
{ \
if (!_Py_TracingPossible(ceval2) && !PyDTrace_LINE_ENABLED()) { \
f->f_lasti = INSTR_OFFSET(); \
NEXTOPARG(); \
goto *opcode_targets[opcode]; \
} \
goto fast_next_opcode; \
}
#endif
#define TARGET(op) op: TARGET_##op
#define DISPATCH_GOTO() goto *opcode_targets[opcode]
#else
#define TARGET(op) op
#define DISPATCH() goto fast_next_opcode
#define DISPATCH_GOTO() goto dispatch_opcode
#endif
#define DISPATCH() \
{ \
if (_Py_TracingPossible(ceval2) OR_DTRACE_LINE OR_LLTRACE) { \
goto tracing_dispatch; \
} \
f->f_lasti = INSTR_OFFSET(); \
NEXTOPARG(); \
DISPATCH_GOTO(); \
}
#define CHECK_EVAL_BREAKER() \
if (_Py_atomic_load_relaxed(eval_breaker)) { \
continue; \
@ -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;
}
}
}
fast_next_opcode:
tracing_dispatch:
f->f_lasti = INSTR_OFFSET();
NEXTOPARG();
if (PyDTrace_LINE_ENABLED())
maybe_dtrace_line(f, &trace_info);
@ -1805,22 +1798,12 @@ 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;
}
/* 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)