summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMark Shannon <mark@hotpy.org>2021-04-08 10:22:55 (GMT)
committerGitHub <noreply@github.com>2021-04-08 10:22:55 (GMT)
commit28d28e053db6b69d91c2dfd579207cd8ccbc39e7 (patch)
treebc37ffc6640173d75b449b6dcb11c2bceae6e4a1
parentb98eba5bc2ffbe7a0ed49d540ebc4f756ae61985 (diff)
downloadcpython-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.c113
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)