Skip to content

Commit 28d28e0

Browse files
authored
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.
1 parent b98eba5 commit 28d28e0

File tree

1 file changed

+52
-61
lines changed

1 file changed

+52
-61
lines changed

Python/ceval.c

+52-61
Original file line numberDiff line numberDiff line change
@@ -1264,6 +1264,23 @@ eval_frame_handle_pending(PyThreadState *tstate)
12641264
-fno-crossjumping).
12651265
*/
12661266

1267+
/* Use macros rather than inline functions, to make it as clear as possible
1268+
* to the C compiler that the tracing check is a simple test then branch.
1269+
* We want to be sure that the compiler knows this before it generates
1270+
* the CFG.
1271+
*/
1272+
#ifdef LLTRACE
1273+
#define OR_LLTRACE || lltrace
1274+
#else
1275+
#define OR_LLTRACE
1276+
#endif
1277+
1278+
#ifdef WITH_DTRACE
1279+
#define OR_DTRACE_LINE || PyDTrace_LINE_ENABLED()
1280+
#else
1281+
#define OR_DTRACE_LINE
1282+
#endif
1283+
12671284
#ifdef DYNAMIC_EXECUTION_PROFILE
12681285
#undef USE_COMPUTED_GOTOS
12691286
#define USE_COMPUTED_GOTOS 0
@@ -1282,37 +1299,22 @@ eval_frame_handle_pending(PyThreadState *tstate)
12821299
#endif
12831300

12841301
#if USE_COMPUTED_GOTOS
1285-
#define TARGET(op) \
1286-
op: \
1287-
TARGET_##op
1288-
1289-
#ifdef LLTRACE
1290-
#define DISPATCH() \
1291-
{ \
1292-
if (!lltrace && !_Py_TracingPossible(ceval2) && !PyDTrace_LINE_ENABLED()) { \
1293-
f->f_lasti = INSTR_OFFSET(); \
1294-
NEXTOPARG(); \
1295-
goto *opcode_targets[opcode]; \
1296-
} \
1297-
goto fast_next_opcode; \
1298-
}
1302+
#define TARGET(op) op: TARGET_##op
1303+
#define DISPATCH_GOTO() goto *opcode_targets[opcode]
12991304
#else
1305+
#define TARGET(op) op
1306+
#define DISPATCH_GOTO() goto dispatch_opcode
1307+
#endif
1308+
13001309
#define DISPATCH() \
13011310
{ \
1302-
if (!_Py_TracingPossible(ceval2) && !PyDTrace_LINE_ENABLED()) { \
1303-
f->f_lasti = INSTR_OFFSET(); \
1304-
NEXTOPARG(); \
1305-
goto *opcode_targets[opcode]; \
1311+
if (_Py_TracingPossible(ceval2) OR_DTRACE_LINE OR_LLTRACE) { \
1312+
goto tracing_dispatch; \
13061313
} \
1307-
goto fast_next_opcode; \
1314+
f->f_lasti = INSTR_OFFSET(); \
1315+
NEXTOPARG(); \
1316+
DISPATCH_GOTO(); \
13081317
}
1309-
#endif
1310-
1311-
#else
1312-
#define TARGET(op) op
1313-
#define DISPATCH() goto fast_next_opcode
1314-
1315-
#endif
13161318

13171319
#define CHECK_EVAL_BREAKER() \
13181320
if (_Py_atomic_load_relaxed(eval_breaker)) { \
@@ -1598,14 +1600,6 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, PyFrameObject *f, int throwflag)
15981600
_Py_atomic_int * const eval_breaker = &ceval2->eval_breaker;
15991601
PyCodeObject *co;
16001602

1601-
/* when tracing we set things up so that
1602-
1603-
not (instr_lb <= current_bytecode_offset < instr_ub)
1604-
1605-
is true when the line being executed has changed. The
1606-
initial values are such as to make this false the first
1607-
time it is tested. */
1608-
16091603
const _Py_CODEUNIT *first_instr;
16101604
PyObject *names;
16111605
PyObject *consts;
@@ -1620,7 +1614,7 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, PyFrameObject *f, int throwflag)
16201614
}
16211615

16221616
PyTraceInfo trace_info;
1623-
/* Mark trace_info as initialized */
1617+
/* Mark trace_info as uninitialized */
16241618
trace_info.code = NULL;
16251619

16261620
/* push frame */
@@ -1754,10 +1748,10 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, PyFrameObject *f, int throwflag)
17541748

17551749
if (_Py_atomic_load_relaxed(eval_breaker)) {
17561750
opcode = _Py_OPCODE(*next_instr);
1757-
if (opcode == SETUP_FINALLY ||
1758-
opcode == SETUP_WITH ||
1759-
opcode == BEFORE_ASYNC_WITH ||
1760-
opcode == YIELD_FROM) {
1751+
if (opcode != SETUP_FINALLY &&
1752+
opcode != SETUP_WITH &&
1753+
opcode != BEFORE_ASYNC_WITH &&
1754+
opcode != YIELD_FROM) {
17611755
/* Few cases where we skip running signal handlers and other
17621756
pending calls:
17631757
- If we're about to enter the 'with:'. It will prevent
@@ -1774,16 +1768,15 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, PyFrameObject *f, int throwflag)
17741768
running the signal handler and raising KeyboardInterrupt
17751769
(see bpo-30039).
17761770
*/
1777-
goto fast_next_opcode;
1778-
}
1779-
1780-
if (eval_frame_handle_pending(tstate) != 0) {
1781-
goto error;
1782-
}
1771+
if (eval_frame_handle_pending(tstate) != 0) {
1772+
goto error;
1773+
}
1774+
}
17831775
}
17841776

1785-
fast_next_opcode:
1777+
tracing_dispatch:
17861778
f->f_lasti = INSTR_OFFSET();
1779+
NEXTOPARG();
17871780

17881781
if (PyDTrace_LINE_ENABLED())
17891782
maybe_dtrace_line(f, &trace_info);
@@ -1805,23 +1798,13 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, PyFrameObject *f, int throwflag)
18051798
JUMPTO(f->f_lasti);
18061799
stack_pointer = f->f_valuestack+f->f_stackdepth;
18071800
f->f_stackdepth = -1;
1808-
if (err)
1801+
if (err) {
18091802
/* trace function raised an exception */
18101803
goto error;
1804+
}
1805+
NEXTOPARG();
18111806
}
18121807

1813-
/* Extract opcode and argument */
1814-
1815-
NEXTOPARG();
1816-
dispatch_opcode:
1817-
#ifdef DYNAMIC_EXECUTION_PROFILE
1818-
#ifdef DXPAIRS
1819-
dxpairs[lastopcode][opcode]++;
1820-
lastopcode = opcode;
1821-
#endif
1822-
dxp[opcode]++;
1823-
#endif
1824-
18251808
#ifdef LLTRACE
18261809
/* Instruction tracing */
18271810

@@ -1837,11 +1820,20 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, PyFrameObject *f, int throwflag)
18371820
}
18381821
#endif
18391822

1823+
dispatch_opcode:
1824+
#ifdef DYNAMIC_EXECUTION_PROFILE
1825+
#ifdef DXPAIRS
1826+
dxpairs[lastopcode][opcode]++;
1827+
lastopcode = opcode;
1828+
#endif
1829+
dxp[opcode]++;
1830+
#endif
1831+
18401832
switch (opcode) {
18411833

18421834
/* BEWARE!
18431835
It is essential that any operation that fails must goto error
1844-
and that all operation that succeed call [FAST_]DISPATCH() ! */
1836+
and that all operation that succeed call DISPATCH() ! */
18451837

18461838
case TARGET(NOP): {
18471839
DISPATCH();
@@ -5427,7 +5419,6 @@ unpack_iterable(PyThreadState *tstate, PyObject *v,
54275419
return 0;
54285420
}
54295421

5430-
54315422
#ifdef LLTRACE
54325423
static int
54335424
prtrace(PyThreadState *tstate, PyObject *v, const char *str)

0 commit comments

Comments
 (0)