Skip to content

Commit 82d2cd0

Browse files
committed
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.
1 parent 1be456a commit 82d2cd0

File tree

1 file changed

+54
-61
lines changed

1 file changed

+54
-61
lines changed

Python/ceval.c

+54-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
@@ -1285,34 +1302,21 @@ eval_frame_handle_pending(PyThreadState *tstate)
12851302
#define TARGET(op) \
12861303
op: \
12871304
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-
}
1305+
#define DISPATCH_GOTO goto *opcode_targets[opcode];
12991306
#else
1307+
#define TARGET(op) op
1308+
#define DISPATCH_GOTO goto dispatch_opcode;
1309+
#endif
1310+
13001311
#define DISPATCH() \
13011312
{ \
1302-
if (!_Py_TracingPossible(ceval2) && !PyDTrace_LINE_ENABLED()) { \
1303-
f->f_lasti = INSTR_OFFSET(); \
1304-
NEXTOPARG(); \
1305-
goto *opcode_targets[opcode]; \
1313+
if (_Py_TracingPossible(ceval2) OR_DTRACE_LINE OR_LLTRACE) { \
1314+
goto tracing_dispatch; \
13061315
} \
1307-
goto fast_next_opcode; \
1316+
f->f_lasti = INSTR_OFFSET(); \
1317+
NEXTOPARG(); \
1318+
DISPATCH_GOTO \
13081319
}
1309-
#endif
1310-
1311-
#else
1312-
#define TARGET(op) op
1313-
#define DISPATCH() goto fast_next_opcode
1314-
1315-
#endif
13161320

13171321
#define CHECK_EVAL_BREAKER() \
13181322
if (_Py_atomic_load_relaxed(eval_breaker)) { \
@@ -1590,22 +1594,14 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, PyFrameObject *f, int throwflag)
15901594
#endif
15911595
PyObject **stack_pointer; /* Next free slot in value stack */
15921596
const _Py_CODEUNIT *next_instr;
1593-
int opcode; /* Current opcode */
1597+
uint8_t opcode; /* Current opcode */
15941598
int oparg; /* Current opcode argument, if any */
15951599
PyObject **fastlocals, **freevars;
15961600
PyObject *retval = NULL; /* Return value */
15971601
struct _ceval_state * const ceval2 = &tstate->interp->ceval;
15981602
_Py_atomic_int * const eval_breaker = &ceval2->eval_breaker;
15991603
PyCodeObject *co;
16001604

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-
16091605
const _Py_CODEUNIT *first_instr;
16101606
PyObject *names;
16111607
PyObject *consts;
@@ -1620,7 +1616,7 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, PyFrameObject *f, int throwflag)
16201616
}
16211617

16221618
PyTraceInfo trace_info;
1623-
/* Mark trace_info as initialized */
1619+
/* Mark trace_info as uninitialized */
16241620
trace_info.code = NULL;
16251621

16261622
/* push frame */
@@ -1754,10 +1750,10 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, PyFrameObject *f, int throwflag)
17541750

17551751
if (_Py_atomic_load_relaxed(eval_breaker)) {
17561752
opcode = _Py_OPCODE(*next_instr);
1757-
if (opcode == SETUP_FINALLY ||
1758-
opcode == SETUP_WITH ||
1759-
opcode == BEFORE_ASYNC_WITH ||
1760-
opcode == YIELD_FROM) {
1753+
if (opcode != SETUP_FINALLY &&
1754+
opcode != SETUP_WITH &&
1755+
opcode != BEFORE_ASYNC_WITH &&
1756+
opcode != YIELD_FROM) {
17611757
/* Few cases where we skip running signal handlers and other
17621758
pending calls:
17631759
- If we're about to enter the 'with:'. It will prevent
@@ -1774,16 +1770,15 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, PyFrameObject *f, int throwflag)
17741770
running the signal handler and raising KeyboardInterrupt
17751771
(see bpo-30039).
17761772
*/
1777-
goto fast_next_opcode;
1778-
}
1779-
1780-
if (eval_frame_handle_pending(tstate) != 0) {
1781-
goto error;
1782-
}
1773+
if (eval_frame_handle_pending(tstate) != 0) {
1774+
goto error;
1775+
}
1776+
}
17831777
}
17841778

1785-
fast_next_opcode:
1779+
tracing_dispatch:
17861780
f->f_lasti = INSTR_OFFSET();
1781+
NEXTOPARG();
17871782

17881783
if (PyDTrace_LINE_ENABLED())
17891784
maybe_dtrace_line(f, &trace_info);
@@ -1801,27 +1796,17 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, PyFrameObject *f, int throwflag)
18011796
tstate->c_traceobj,
18021797
tstate, f,
18031798
&trace_info);
1799+
if (err) {
1800+
/* trace function raised an exception */
1801+
goto error;
1802+
}
18041803
/* Reload possibly changed frame fields */
18051804
JUMPTO(f->f_lasti);
18061805
stack_pointer = f->f_valuestack+f->f_stackdepth;
18071806
f->f_stackdepth = -1;
1808-
if (err)
1809-
/* trace function raised an exception */
1810-
goto error;
1807+
NEXTOPARG();
18111808
}
18121809

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-
18251810
#ifdef LLTRACE
18261811
/* Instruction tracing */
18271812

@@ -1837,11 +1822,20 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, PyFrameObject *f, int throwflag)
18371822
}
18381823
#endif
18391824

1825+
dispatch_opcode:
1826+
#ifdef DYNAMIC_EXECUTION_PROFILE
1827+
#ifdef DXPAIRS
1828+
dxpairs[lastopcode][opcode]++;
1829+
lastopcode = opcode;
1830+
#endif
1831+
dxp[opcode]++;
1832+
#endif
1833+
18401834
switch (opcode) {
18411835

18421836
/* BEWARE!
18431837
It is essential that any operation that fails must goto error
1844-
and that all operation that succeed call [FAST_]DISPATCH() ! */
1838+
and that all operation that succeed call DISPATCH() ! */
18451839

18461840
case TARGET(NOP): {
18471841
DISPATCH();
@@ -5427,7 +5421,6 @@ unpack_iterable(PyThreadState *tstate, PyObject *v,
54275421
return 0;
54285422
}
54295423

5430-
54315424
#ifdef LLTRACE
54325425
static int
54335426
prtrace(PyThreadState *tstate, PyObject *v, const char *str)

0 commit comments

Comments
 (0)