mirror of
https://github.com/python/cpython.git
synced 2025-10-10 00:43:41 +00:00
gh-111520: Integrate the Tier 2 interpreter in the Tier 1 interpreter (#111428)
- There is no longer a separate Python/executor.c file. - Conventions in Python/bytecodes.c are slightly different -- don't use `goto error`, you must use `GOTO_ERROR(error)` (same for others like `unused_local_error`). - The `TIER_ONE` and `TIER_TWO` symbols are only valid in the generated (.c.h) files. - In Lib/test/support/__init__.py, `Py_C_RECURSION_LIMIT` is imported from `_testcapi`. - On Windows, in debug mode, stack allocation grows from 8MiB to 12MiB. - **Beware!** This changes the env vars to enable uops and their debugging to `PYTHON_UOPS` and `PYTHON_LLTRACE`.
This commit is contained in:
parent
5d6db168b9
commit
7e135a48d6
19 changed files with 509 additions and 487 deletions
156
Python/ceval.c
156
Python/ceval.c
|
@ -202,15 +202,15 @@ maybe_lltrace_resume_frame(_PyInterpreterFrame *frame, _PyInterpreterFrame *skip
|
|||
if (r < 0) {
|
||||
return -1;
|
||||
}
|
||||
int lltrace = r;
|
||||
int lltrace = r * 5; // Levels 1-4 only trace uops
|
||||
if (!lltrace) {
|
||||
// When tracing executed uops, also trace bytecode
|
||||
char *uop_debug = Py_GETENV("PYTHONUOPSDEBUG");
|
||||
if (uop_debug != NULL && *uop_debug >= '0') {
|
||||
lltrace = (*uop_debug - '0') >= 5; // TODO: Parse an int and all that
|
||||
// Can also be controlled by environment variable
|
||||
char *python_lltrace = Py_GETENV("PYTHON_LLTRACE");
|
||||
if (python_lltrace != NULL && *python_lltrace >= '0') {
|
||||
lltrace = *python_lltrace - '0'; // TODO: Parse an int and all that
|
||||
}
|
||||
}
|
||||
if (lltrace) {
|
||||
if (lltrace >= 5) {
|
||||
lltrace_resume_frame(frame);
|
||||
}
|
||||
return lltrace;
|
||||
|
@ -611,10 +611,8 @@ PyEval_EvalFrameEx(PyFrameObject *f, int throwflag)
|
|||
return _PyEval_EvalFrame(tstate, f->f_frame, throwflag);
|
||||
}
|
||||
|
||||
#define TIER_ONE 1
|
||||
#include "ceval_macros.h"
|
||||
|
||||
|
||||
int _Py_CheckRecursiveCallPy(
|
||||
PyThreadState *tstate)
|
||||
{
|
||||
|
@ -680,9 +678,7 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int
|
|||
#ifdef Py_STATS
|
||||
int lastopcode = 0;
|
||||
#endif
|
||||
// opcode is an 8-bit value to improve the code generated by MSVC
|
||||
// for the big switch below (in combination with the EXTRA_CASES macro).
|
||||
uint8_t opcode; /* Current opcode */
|
||||
int opcode; /* Current opcode */
|
||||
int oparg; /* Current opcode argument, if any */
|
||||
#ifdef LLTRACE
|
||||
int lltrace = 0;
|
||||
|
@ -730,6 +726,9 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int
|
|||
goto resume_with_error;
|
||||
}
|
||||
|
||||
/* State shared between Tier 1 and Tier 2 interpreter */
|
||||
_PyUOpExecutorObject *current_executor = NULL;
|
||||
|
||||
/* Local "register" variables.
|
||||
* These are cached values from the frame and code object. */
|
||||
|
||||
|
@ -766,7 +765,9 @@ resume_frame:
|
|||
/* Start instructions */
|
||||
#if !USE_COMPUTED_GOTOS
|
||||
dispatch_opcode:
|
||||
switch (opcode)
|
||||
// Cast to an 8-bit value to improve the code generated by MSVC
|
||||
// (in combination with the EXTRA_CASES macro).
|
||||
switch ((uint8_t)opcode)
|
||||
#endif
|
||||
{
|
||||
|
||||
|
@ -914,7 +915,7 @@ exception_unwind:
|
|||
}
|
||||
/* Resume normal execution */
|
||||
#ifdef LLTRACE
|
||||
if (lltrace) {
|
||||
if (lltrace >= 5) {
|
||||
lltrace_resume_frame(frame);
|
||||
}
|
||||
#endif
|
||||
|
@ -943,6 +944,135 @@ resume_with_error:
|
|||
stack_pointer = _PyFrame_GetStackPointer(frame);
|
||||
goto error;
|
||||
|
||||
|
||||
|
||||
// The Tier 2 interpreter is also here!
|
||||
enter_tier_two:
|
||||
|
||||
#undef LOAD_IP
|
||||
#define LOAD_IP(UNUSED) (void)0
|
||||
|
||||
#undef GOTO_ERROR
|
||||
#define GOTO_ERROR(LABEL) goto LABEL ## _tier_two
|
||||
|
||||
#undef DEOPT_IF
|
||||
#define DEOPT_IF(COND, INSTNAME) \
|
||||
if ((COND)) { \
|
||||
goto deoptimize;\
|
||||
}
|
||||
|
||||
#ifdef Py_STATS
|
||||
// Disable these macros that apply to Tier 1 stats when we are in Tier 2
|
||||
#undef STAT_INC
|
||||
#define STAT_INC(opname, name) ((void)0)
|
||||
#undef STAT_DEC
|
||||
#define STAT_DEC(opname, name) ((void)0)
|
||||
#undef CALL_STAT_INC
|
||||
#define CALL_STAT_INC(name) ((void)0)
|
||||
#endif
|
||||
|
||||
#undef ENABLE_SPECIALIZATION
|
||||
#define ENABLE_SPECIALIZATION 0
|
||||
|
||||
#ifdef Py_DEBUG
|
||||
#define DPRINTF(level, ...) \
|
||||
if (lltrace >= (level)) { printf(__VA_ARGS__); }
|
||||
#else
|
||||
#define DPRINTF(level, ...)
|
||||
#endif
|
||||
|
||||
OPT_STAT_INC(traces_executed);
|
||||
_PyUOpInstruction *next_uop = current_executor->trace;
|
||||
#ifdef Py_DEBUG
|
||||
uint64_t operand; // Used by several DPRINTF() calls
|
||||
#endif
|
||||
#ifdef Py_STATS
|
||||
uint64_t trace_uop_execution_counter = 0;
|
||||
#endif
|
||||
|
||||
for (;;) {
|
||||
opcode = next_uop->opcode;
|
||||
oparg = next_uop->oparg;
|
||||
#ifdef Py_DEBUG
|
||||
operand = next_uop->operand;
|
||||
#endif
|
||||
DPRINTF(3,
|
||||
"%4d: uop %s, oparg %d, operand %" PRIu64 ", stack_level %d\n",
|
||||
(int)(next_uop - current_executor->trace),
|
||||
opcode < 256 ? _PyOpcode_OpName[opcode] : _PyOpcode_uop_name[opcode],
|
||||
oparg,
|
||||
operand,
|
||||
(int)(stack_pointer - _PyFrame_Stackbase(frame)));
|
||||
next_uop++;
|
||||
OPT_STAT_INC(uops_executed);
|
||||
#ifdef Py_STATS
|
||||
trace_uop_execution_counter++;
|
||||
#endif
|
||||
|
||||
switch (opcode) {
|
||||
|
||||
#include "executor_cases.c.h"
|
||||
|
||||
default:
|
||||
#ifdef Py_DEBUG
|
||||
{
|
||||
fprintf(stderr, "Unknown uop %d, oparg %d, operand %" PRIu64 "\n",
|
||||
opcode, oparg, operand);
|
||||
Py_FatalError("Unknown uop");
|
||||
}
|
||||
#else
|
||||
Py_UNREACHABLE();
|
||||
#endif
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
// Jump here from ERROR_IF(..., unbound_local_error)
|
||||
unbound_local_error_tier_two:
|
||||
_PyEval_FormatExcCheckArg(tstate, PyExc_UnboundLocalError,
|
||||
UNBOUNDLOCAL_ERROR_MSG,
|
||||
PyTuple_GetItem(_PyFrame_GetCode(frame)->co_localsplusnames, oparg)
|
||||
);
|
||||
goto error_tier_two;
|
||||
|
||||
// JUMP to any of these from ERROR_IF(..., error)
|
||||
pop_4_error_tier_two:
|
||||
STACK_SHRINK(1);
|
||||
pop_3_error_tier_two:
|
||||
STACK_SHRINK(1);
|
||||
pop_2_error_tier_two:
|
||||
STACK_SHRINK(1);
|
||||
pop_1_error_tier_two:
|
||||
STACK_SHRINK(1);
|
||||
error_tier_two:
|
||||
DPRINTF(2, "Error: [Opcode %d, operand %" PRIu64 "]\n", opcode, operand);
|
||||
OPT_HIST(trace_uop_execution_counter, trace_run_length_hist);
|
||||
frame->return_offset = 0; // Don't leave this random
|
||||
_PyFrame_SetStackPointer(frame, stack_pointer);
|
||||
Py_DECREF(current_executor);
|
||||
goto resume_with_error;
|
||||
|
||||
// Jump here from DEOPT_IF()
|
||||
deoptimize:
|
||||
// On DEOPT_IF we just repeat the last instruction.
|
||||
// This presumes nothing was popped from the stack (nor pushed).
|
||||
DPRINTF(2, "DEOPT: [Opcode %d, operand %" PRIu64 "]\n", opcode, operand);
|
||||
OPT_HIST(trace_uop_execution_counter, trace_run_length_hist);
|
||||
frame->return_offset = 0; // Dispatch to frame->instr_ptr
|
||||
_PyFrame_SetStackPointer(frame, stack_pointer);
|
||||
Py_DECREF(current_executor);
|
||||
// Fall through
|
||||
// Jump here from ENTER_EXECUTOR
|
||||
enter_tier_one:
|
||||
next_instr = frame->instr_ptr;
|
||||
goto resume_frame;
|
||||
|
||||
// Jump here from _EXIT_TRACE
|
||||
exit_trace:
|
||||
_PyFrame_SetStackPointer(frame, stack_pointer);
|
||||
Py_DECREF(current_executor);
|
||||
OPT_HIST(trace_uop_execution_counter, trace_run_length_hist);
|
||||
goto enter_tier_one;
|
||||
}
|
||||
#if defined(__GNUC__)
|
||||
# pragma GCC diagnostic pop
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue