Skip to content

Commit 692a992

Browse files
Fix INTERPRETER_EXIT tracing
1 parent da66058 commit 692a992

12 files changed

+148
-90
lines changed

Include/internal/pycore_ceval.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -391,6 +391,9 @@ _PyForIter_VirtualIteratorNext(PyThreadState* tstate, struct _PyInterpreterFrame
391391
#define SPECIAL___AEXIT__ 3
392392
#define SPECIAL_MAX 3
393393

394+
struct _PyCode12 _PyCode_DEF(12);
395+
PyAPI_DATA(const struct _PyCode12) _PyEntryFrameCode;
396+
394397
#ifdef __cplusplus
395398
}
396399
#endif

Include/internal/pycore_code.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -672,6 +672,12 @@ PyAPI_FUNC(int) _PyCode_ReturnsOnlyNone(PyCodeObject *);
672672
* compare bytes and str which can raise a BytesWarning exception. */
673673
extern PyObject* _PyCode_ConstantKey(PyObject *obj);
674674

675+
#define NO_LOC_4 (128 | (PY_CODE_LOCATION_INFO_NONE << 3) | 3)
676+
677+
static const PyBytesObject no_location = {
678+
PyVarObject_HEAD_INIT(&PyBytes_Type, 1)
679+
.ob_sval = { NO_LOC_4 }
680+
};
675681

676682
#ifdef __cplusplus
677683
}

Include/internal/pycore_global_objects_fini_generated.h

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Include/internal/pycore_global_strings.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,7 @@ struct _Py_global_strings {
6767
STRUCT_FOR_ID(TextIOWrapper)
6868
STRUCT_FOR_ID(True)
6969
STRUCT_FOR_ID(WarningMessage)
70+
STRUCT_FOR_ID(_PyEval_EvalFrameDefault)
7071
STRUCT_FOR_ID(_WindowsConsoleIO)
7172
STRUCT_FOR_ID(__IOBase_closed)
7273
STRUCT_FOR_ID(__abc_tpflags__)

Include/internal/pycore_opcode_metadata.h

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Include/internal/pycore_runtime_init_generated.h

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Include/internal/pycore_unicodeobject_generated.h

Lines changed: 4 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Python/bytecodes.c

Lines changed: 33 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -170,7 +170,9 @@ dummy_func(
170170

171171
op(_QUICKEN_RESUME, (--)) {
172172
#if ENABLE_SPECIALIZATION_FT
173-
if (tstate->tracing == 0 && this_instr->op.code == RESUME) {
173+
PyCodeObject *code = _PyFrame_GetCode(frame);
174+
if (tstate->tracing == 0 && this_instr->op.code == RESUME &&
175+
code != (PyCodeObject *)&_Py_InitCleanup && code != (PyCodeObject *)&_PyEntryFrameCode) {
174176
FT_ATOMIC_STORE_UINT8_RELAXED(this_instr->op.code, RESUME_CHECK);
175177
}
176178
#endif /* ENABLE_SPECIALIZATION_FT */
@@ -1218,19 +1220,33 @@ dummy_func(
12181220
tstate->current_frame = frame->previous;
12191221
assert(!_PyErr_Occurred(tstate));
12201222
PyObject *result = PyStackRef_AsPyObjectSteal(retval);
1223+
if (IS_JIT_TRACING()) {
1224+
#if _Py_TIER2
1225+
_PyJit_translate_single_bytecode_to_trace(tstate, frame, next_instr);
1226+
LEAVE_TRACING();
1227+
int err = bail_tracing_and_jit(tstate, frame);
1228+
if (err < 0) {
1229+
Py_DECREF(result);
1230+
ERROR_IF(true);
1231+
}
1232+
return result;
1233+
#endif
1234+
}
1235+
else {
12211236
#if !_Py_TAIL_CALL_INTERP
1222-
assert(frame == &entry.frame);
1237+
assert(frame == &entry.frame);
12231238
#endif
12241239
#ifdef _Py_TIER2
1225-
_PyStackRef executor = frame->localsplus[0];
1226-
assert(tstate->current_executor == NULL);
1227-
if (!PyStackRef_IsNull(executor)) {
1228-
tstate->current_executor = PyStackRef_AsPyObjectBorrow(executor);
1229-
PyStackRef_CLOSE(executor);
1230-
}
1240+
_PyStackRef executor = frame->localsplus[0];
1241+
assert(tstate->current_executor == NULL);
1242+
if (!PyStackRef_IsNull(executor)) {
1243+
tstate->current_executor = PyStackRef_AsPyObjectBorrow(executor);
1244+
PyStackRef_CLOSE(executor);
1245+
}
12311246
#endif
1232-
LLTRACE_RESUME_FRAME();
1233-
return result;
1247+
LLTRACE_RESUME_FRAME();
1248+
return result;
1249+
}
12341250
}
12351251

12361252
// The stack effect here is a bit misleading.
@@ -3020,15 +3036,6 @@ dummy_func(
30203036

30213037
tier1 inst(ENTER_EXECUTOR, (--)) {
30223038
#ifdef _Py_TIER2
3023-
// We want to end any current trace here, before we possibly need
3024-
// to start tracing new ones due to recursive traces in any inner C functions
3025-
// in tier2 code.
3026-
if (IS_JIT_TRACING()) {
3027-
_PyJit_translate_single_bytecode_to_trace(tstate, frame, next_instr);
3028-
LEAVE_TRACING();
3029-
int err = bail_tracing_and_jit(tstate, frame);
3030-
ERROR_IF(err < 0);
3031-
}
30323039
PyCodeObject *code = _PyFrame_GetCode(frame);
30333040
_PyExecutorObject *executor = code->co_executors->executors[oparg & 255];
30343041
assert(executor->vm_data.index == INSTR_OFFSET() - 1);
@@ -3038,14 +3045,19 @@ dummy_func(
30383045
/* If the eval breaker is set then stay in tier 1.
30393046
* This avoids any potentially infinite loops
30403047
* involving _RESUME_CHECK */
3041-
if (_Py_atomic_load_uintptr_relaxed(&tstate->eval_breaker) & _PY_EVAL_EVENTS_MASK) {
3048+
if (IS_JIT_TRACING() || _Py_atomic_load_uintptr_relaxed(&tstate->eval_breaker) & _PY_EVAL_EVENTS_MASK) {
30423049
opcode = executor->vm_data.opcode;
30433050
oparg = (oparg & ~255) | executor->vm_data.oparg;
30443051
next_instr = this_instr;
30453052
if (_PyOpcode_Caches[_PyOpcode_Deopt[opcode]]) {
30463053
PAUSE_ADAPTIVE_COUNTER(this_instr[1].counter);
30473054
}
3048-
DISPATCH_GOTO();
3055+
if (IS_JIT_TRACING()) {
3056+
DISPATCH_GOTO_NON_TRACING();
3057+
}
3058+
else {
3059+
DISPATCH_GOTO();
3060+
}
30493061
}
30503062
assert(executor != tstate->interp->cold_executor);
30513063
tstate->jit_exit = NULL;

Python/ceval.c

Lines changed: 42 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -931,14 +931,39 @@ int _Py_CheckRecursiveCallPy(
931931
return 0;
932932
}
933933

934-
static const _Py_CODEUNIT _Py_INTERPRETER_TRAMPOLINE_INSTRUCTIONS[] = {
935-
/* Put a NOP at the start, so that the IP points into
936-
* the code, rather than before it */
937-
{ .op.code = NOP, .op.arg = 0 },
938-
{ .op.code = INTERPRETER_EXIT, .op.arg = 0 }, /* reached on return */
939-
{ .op.code = NOP, .op.arg = 0 },
940-
{ .op.code = INTERPRETER_EXIT, .op.arg = 0 }, /* reached on yield */
941-
{ .op.code = RESUME, .op.arg = RESUME_OPARG_DEPTH1_MASK | RESUME_AT_FUNC_START }
934+
935+
#ifdef Py_GIL_DISABLED
936+
static _PyCodeArray emtry_cleanup_tlbc = {
937+
.size = 1,
938+
.entries = {(char*) &_PyEntryFrameCode.co_code_adaptive},
939+
};
940+
#endif
941+
942+
const struct _PyCode12 _PyEntryFrameCode = {
943+
_PyVarObject_HEAD_INIT(&PyCode_Type, 5),
944+
.co_consts = (PyObject *)&_Py_SINGLETON(tuple_empty),
945+
.co_names = (PyObject *)&_Py_SINGLETON(tuple_empty),
946+
.co_exceptiontable = (PyObject *)&_Py_SINGLETON(bytes_empty),
947+
.co_flags = CO_OPTIMIZED | CO_NO_MONITORING_EVENTS,
948+
.co_localsplusnames = (PyObject *)&_Py_SINGLETON(tuple_empty),
949+
.co_localspluskinds = (PyObject *)&_Py_SINGLETON(bytes_empty),
950+
.co_filename = &_Py_ID(_PyEval_EvalFrameDefault),
951+
.co_name = &_Py_ID(_PyEval_EvalFrameDefault),
952+
.co_qualname = &_Py_ID(_PyEval_EvalFrameDefault),
953+
.co_linetable = (PyObject *)&no_location,
954+
._co_firsttraceable = 4,
955+
.co_stacksize = 2,
956+
.co_framesize = 2 + FRAME_SPECIALS_SIZE,
957+
#ifdef Py_GIL_DISABLED
958+
.co_tlbc = &emtry_cleanup_tlbc,
959+
#endif
960+
.co_code_adaptive = {
961+
NOP, 0,
962+
INTERPRETER_EXIT, 0, /* reached on return */
963+
NOP, 0,
964+
INTERPRETER_EXIT, 0, /* reached on yield */
965+
RESUME, RESUME_OPARG_DEPTH1_MASK | RESUME_AT_FUNC_START
966+
}
942967
};
943968

944969
#ifdef Py_DEBUG
@@ -1014,7 +1039,13 @@ bail_tracing_and_jit(PyThreadState *tstate, _PyInterpreterFrame *frame)
10141039
// Likewise, we hold a strong reference to the executor containing this exit, so the exit is guaranteed
10151040
// to be valid to access.
10161041
if (err <= 0) {
1017-
exit->temperature = restart_backoff_counter(exit->temperature);
1042+
// Some opcodes will forever be unchanged. Don't ever bother specializing for them ever again.
1043+
if (tstate->interp->jit_state.prev_instr->op.code == INTERPRETER_EXIT) {
1044+
exit->temperature = initial_unreachable_backoff_counter();
1045+
}
1046+
else {
1047+
exit->temperature = restart_backoff_counter(exit->temperature);
1048+
}
10181049
}
10191050
else {
10201051
exit->temperature = initial_temperature_backoff_counter();
@@ -1105,8 +1136,8 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int
11051136
entry.frame.f_globals = (PyObject*)0xaaa3;
11061137
entry.frame.f_builtins = (PyObject*)0xaaa4;
11071138
#endif
1108-
entry.frame.f_executable = PyStackRef_None;
1109-
entry.frame.instr_ptr = (_Py_CODEUNIT *)_Py_INTERPRETER_TRAMPOLINE_INSTRUCTIONS + 1;
1139+
entry.frame.f_executable = PyStackRef_FromPyObjectBorrow((PyObject *)&_PyEntryFrameCode);
1140+
entry.frame.instr_ptr = ((_Py_CODEUNIT *)_PyEntryFrameCode.co_code_adaptive) + 1;
11101141
entry.frame.stackpointer = entry.stack;
11111142
entry.frame.owner = FRAME_OWNED_BY_INTERPRETER;
11121143
entry.frame.visited = 0;

Python/generated_cases.c.h

Lines changed: 45 additions & 29 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)