Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit ac7d5ba

Browse filesBrowse files
authored
GH-133231: Changes to executor management to support proposed sys._jit module (GH-133287)
* Track the current executor, not the previous one, on the thread-state. * Batch executors for deallocation to avoid having to constantly incref executors; this is an ad-hoc form of deferred reference counting.
1 parent 1d9406e commit ac7d5ba
Copy full SHA for ac7d5ba

13 files changed

+176
-54
lines changed

‎Include/cpython/pystate.h

Copy file name to clipboardExpand all lines: Include/cpython/pystate.h
+1-1Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -194,7 +194,7 @@ struct _ts {
194194
/* The thread's exception stack entry. (Always the last entry.) */
195195
_PyErr_StackItem exc_state;
196196

197-
PyObject *previous_executor;
197+
PyObject *current_executor;
198198

199199
uint64_t dict_global_version;
200200

‎Include/internal/pycore_interp_structs.h

Copy file name to clipboardExpand all lines: Include/internal/pycore_interp_structs.h
+2Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -923,6 +923,8 @@ struct _is {
923923
PyObject *common_consts[NUM_COMMON_CONSTANTS];
924924
bool jit;
925925
struct _PyExecutorObject *executor_list_head;
926+
struct _PyExecutorObject *executor_deletion_list_head;
927+
int executor_deletion_list_remaining_capacity;
926928
size_t trace_run_counter;
927929
_rare_events rare_events;
928930
PyDict_WatchCallback builtins_dict_watcher;

‎Include/internal/pycore_opcode_metadata.h

Copy file name to clipboardExpand all lines: Include/internal/pycore_opcode_metadata.h
+1-1Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

‎Include/internal/pycore_optimizer.h

Copy file name to clipboardExpand all lines: Include/internal/pycore_optimizer.h
+8-1Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@ typedef struct {
6969
typedef struct {
7070
uint32_t target;
7171
_Py_BackoffCounter temperature;
72-
const struct _PyExecutorObject *executor;
72+
struct _PyExecutorObject *executor;
7373
} _PyExitData;
7474

7575
typedef struct _PyExecutorObject {
@@ -84,6 +84,10 @@ typedef struct _PyExecutorObject {
8484
_PyExitData exits[1];
8585
} _PyExecutorObject;
8686

87+
/* If pending deletion list gets large enough, then scan,
88+
* and free any executors that aren't executing
89+
* i.e. any that aren't a thread's current_executor. */
90+
#define EXECUTOR_DELETE_LIST_MAX 100
8791

8892
// Export for '_opcode' shared extension (JIT compiler).
8993
PyAPI_FUNC(_PyExecutorObject*) _Py_GetExecutor(PyCodeObject *code, int offset);
@@ -304,6 +308,9 @@ static inline int is_terminator(const _PyUOpInstruction *uop)
304308
}
305309

306310
PyAPI_FUNC(int) _PyDumpExecutors(FILE *out);
311+
#ifdef _Py_TIER2
312+
extern void _Py_ClearExecutorDeletionList(PyInterpreterState *interp);
313+
#endif
307314

308315
#ifdef __cplusplus
309316
}

‎Include/internal/pycore_uop_metadata.h

Copy file name to clipboardExpand all lines: Include/internal/pycore_uop_metadata.h
+1-1Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

‎Python/bytecodes.c

Copy file name to clipboardExpand all lines: Python/bytecodes.c
+24-10Lines changed: 24 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1169,6 +1169,17 @@ dummy_func(
11691169
tstate->current_frame = frame->previous;
11701170
assert(!_PyErr_Occurred(tstate));
11711171
PyObject *result = PyStackRef_AsPyObjectSteal(retval);
1172+
#if !Py_TAIL_CALL_INTERP
1173+
assert(frame == &entry.frame);
1174+
#endif
1175+
#ifdef _Py_TIER2
1176+
_PyStackRef executor = frame->localsplus[0];
1177+
assert(tstate->current_executor == NULL);
1178+
if (!PyStackRef_IsNull(executor)) {
1179+
tstate->current_executor = PyStackRef_AsPyObjectBorrow(executor);
1180+
PyStackRef_CLOSE(executor);
1181+
}
1182+
#endif
11721183
LLTRACE_RESUME_FRAME();
11731184
return result;
11741185
}
@@ -2912,8 +2923,7 @@ dummy_func(
29122923
}
29132924
else {
29142925
this_instr[1].counter = initial_jump_backoff_counter();
2915-
assert(tstate->previous_executor == NULL);
2916-
tstate->previous_executor = Py_None;
2926+
assert(tstate->current_executor == NULL);
29172927
GOTO_TIER_TWO(executor);
29182928
}
29192929
}
@@ -2965,7 +2975,7 @@ dummy_func(
29652975
assert(executor->vm_data.index == INSTR_OFFSET() - 1);
29662976
assert(executor->vm_data.code == code);
29672977
assert(executor->vm_data.valid);
2968-
assert(tstate->previous_executor == NULL);
2978+
assert(tstate->current_executor == NULL);
29692979
/* If the eval breaker is set then stay in tier 1.
29702980
* This avoids any potentially infinite loops
29712981
* involving _RESUME_CHECK */
@@ -2978,8 +2988,6 @@ dummy_func(
29782988
}
29792989
DISPATCH_GOTO();
29802990
}
2981-
tstate->previous_executor = Py_None;
2982-
Py_INCREF(executor);
29832991
GOTO_TIER_TWO(executor);
29842992
#else
29852993
Py_FatalError("ENTER_EXECUTOR is not supported in this build");
@@ -5254,7 +5262,6 @@ dummy_func(
52545262
exit->temperature = initial_temperature_backoff_counter();
52555263
Py_CLEAR(exit->executor);
52565264
}
5257-
tstate->previous_executor = (PyObject *)current_executor;
52585265
if (exit->executor == NULL) {
52595266
_Py_BackoffCounter temperature = exit->temperature;
52605267
if (!backoff_counter_triggers(temperature)) {
@@ -5277,7 +5284,6 @@ dummy_func(
52775284
}
52785285
exit->executor = executor;
52795286
}
5280-
Py_INCREF(exit->executor);
52815287
GOTO_TIER_TWO(exit->executor);
52825288
}
52835289

@@ -5316,7 +5322,6 @@ dummy_func(
53165322
}
53175323

53185324
tier2 op(_START_EXECUTOR, (executor/4 --)) {
5319-
Py_CLEAR(tstate->previous_executor);
53205325
#ifndef _Py_JIT
53215326
current_executor = (_PyExecutorObject*)executor;
53225327
#endif
@@ -5337,12 +5342,10 @@ dummy_func(
53375342
}
53385343

53395344
tier2 op(_DEOPT, (--)) {
5340-
tstate->previous_executor = (PyObject *)current_executor;
53415345
GOTO_TIER_ONE(_PyFrame_GetBytecode(frame) + CURRENT_TARGET());
53425346
}
53435347

53445348
tier2 op(_ERROR_POP_N, (target/2 --)) {
5345-
tstate->previous_executor = (PyObject *)current_executor;
53465349
assert(oparg == 0);
53475350
frame->instr_ptr = _PyFrame_GetBytecode(frame) + target;
53485351
SYNC_SP();
@@ -5463,6 +5466,17 @@ dummy_func(
54635466
if (frame->owner == FRAME_OWNED_BY_INTERPRETER) {
54645467
/* Restore previous frame and exit */
54655468
tstate->current_frame = frame->previous;
5469+
#if !Py_TAIL_CALL_INTERP
5470+
assert(frame == &entry.frame);
5471+
#endif
5472+
#ifdef _Py_TIER2
5473+
_PyStackRef executor = frame->localsplus[0];
5474+
assert(tstate->current_executor == NULL);
5475+
if (!PyStackRef_IsNull(executor)) {
5476+
tstate->current_executor = PyStackRef_AsPyObjectBorrow(executor);
5477+
PyStackRef_CLOSE(executor);
5478+
}
5479+
#endif
54665480
return NULL;
54675481
}
54685482
next_instr = frame->instr_ptr;

‎Python/ceval.c

Copy file name to clipboardExpand all lines: Python/ceval.c
+31-19Lines changed: 31 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -990,6 +990,11 @@ _PyObjectArray_Free(PyObject **array, PyObject **scratch)
990990
#define DONT_SLP_VECTORIZE
991991
#endif
992992

993+
typedef struct {
994+
_PyInterpreterFrame frame;
995+
_PyStackRef stack[1];
996+
} _PyEntryFrame;
997+
993998
PyObject* _Py_HOT_FUNCTION DONT_SLP_VECTORIZE
994999
_PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int throwflag)
9951000
{
@@ -1009,7 +1014,7 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int
10091014
int oparg; /* Current opcode argument, if any */
10101015
assert(tstate->current_frame == NULL || tstate->current_frame->stackpointer != NULL);
10111016
#endif
1012-
_PyInterpreterFrame entry_frame;
1017+
_PyEntryFrame entry;
10131018

10141019
if (_Py_EnterRecursiveCallTstate(tstate, "")) {
10151020
assert(frame->owner != FRAME_OWNED_BY_INTERPRETER);
@@ -1021,30 +1026,37 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int
10211026
* These are cached values from the frame and code object. */
10221027
_Py_CODEUNIT *next_instr;
10231028
_PyStackRef *stack_pointer;
1024-
entry_frame.localsplus[0] = PyStackRef_NULL;
1029+
entry.stack[0] = PyStackRef_NULL;
10251030
#ifdef Py_STACKREF_DEBUG
1026-
entry_frame.f_funcobj = PyStackRef_None;
1031+
entry.frame.f_funcobj = PyStackRef_None;
10271032
#elif defined(Py_DEBUG)
10281033
/* Set these to invalid but identifiable values for debugging. */
1029-
entry_frame.f_funcobj = (_PyStackRef){.bits = 0xaaa0};
1030-
entry_frame.f_locals = (PyObject*)0xaaa1;
1031-
entry_frame.frame_obj = (PyFrameObject*)0xaaa2;
1032-
entry_frame.f_globals = (PyObject*)0xaaa3;
1033-
entry_frame.f_builtins = (PyObject*)0xaaa4;
1034+
entry.frame.f_funcobj = (_PyStackRef){.bits = 0xaaa0};
1035+
entry.frame.f_locals = (PyObject*)0xaaa1;
1036+
entry.frame.frame_obj = (PyFrameObject*)0xaaa2;
1037+
entry.frame.f_globals = (PyObject*)0xaaa3;
1038+
entry.frame.f_builtins = (PyObject*)0xaaa4;
10341039
#endif
1035-
entry_frame.f_executable = PyStackRef_None;
1036-
entry_frame.instr_ptr = (_Py_CODEUNIT *)_Py_INTERPRETER_TRAMPOLINE_INSTRUCTIONS + 1;
1037-
entry_frame.stackpointer = entry_frame.localsplus;
1038-
entry_frame.owner = FRAME_OWNED_BY_INTERPRETER;
1039-
entry_frame.visited = 0;
1040-
entry_frame.return_offset = 0;
1040+
entry.frame.f_executable = PyStackRef_None;
1041+
entry.frame.instr_ptr = (_Py_CODEUNIT *)_Py_INTERPRETER_TRAMPOLINE_INSTRUCTIONS + 1;
1042+
entry.frame.stackpointer = entry.stack;
1043+
entry.frame.owner = FRAME_OWNED_BY_INTERPRETER;
1044+
entry.frame.visited = 0;
1045+
entry.frame.return_offset = 0;
10411046
#ifdef Py_DEBUG
1042-
entry_frame.lltrace = 0;
1047+
entry.frame.lltrace = 0;
10431048
#endif
10441049
/* Push frame */
1045-
entry_frame.previous = tstate->current_frame;
1046-
frame->previous = &entry_frame;
1050+
entry.frame.previous = tstate->current_frame;
1051+
frame->previous = &entry.frame;
10471052
tstate->current_frame = frame;
1053+
entry.frame.localsplus[0] = PyStackRef_NULL;
1054+
#ifdef _Py_TIER2
1055+
if (tstate->current_executor != NULL) {
1056+
entry.frame.localsplus[0] = PyStackRef_FromPyObjectNew(tstate->current_executor);
1057+
tstate->current_executor = NULL;
1058+
}
1059+
#endif
10481060

10491061
/* support for generator.throw() */
10501062
if (throwflag) {
@@ -1071,9 +1083,9 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int
10711083
stack_pointer = _PyFrame_GetStackPointer(frame);
10721084
#if Py_TAIL_CALL_INTERP
10731085
# if Py_STATS
1074-
return _TAIL_CALL_error(frame, stack_pointer, tstate, next_instr, 0, lastopcode);
1086+
return _TAIL_CALL_error(frame, stack_pointer, tstate, next_instr, 0, lastopcode);
10751087
# else
1076-
return _TAIL_CALL_error(frame, stack_pointer, tstate, next_instr, 0);
1088+
return _TAIL_CALL_error(frame, stack_pointer, tstate, next_instr, 0);
10771089
# endif
10781090
#else
10791091
goto error;

‎Python/ceval_macros.h

Copy file name to clipboardExpand all lines: Python/ceval_macros.h
+6-3Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -359,12 +359,12 @@ _PyFrame_SetStackPointer(frame, stack_pointer)
359359
do { \
360360
OPT_STAT_INC(traces_executed); \
361361
_PyExecutorObject *_executor = (EXECUTOR); \
362+
tstate->current_executor = (PyObject *)_executor; \
362363
jit_func jitted = _executor->jit_code; \
363364
/* Keep the shim frame alive via the executor: */ \
364365
Py_INCREF(_executor); \
365366
next_instr = jitted(frame, stack_pointer, tstate); \
366367
Py_DECREF(_executor); \
367-
Py_CLEAR(tstate->previous_executor); \
368368
frame = tstate->current_frame; \
369369
stack_pointer = _PyFrame_GetStackPointer(frame); \
370370
if (next_instr == NULL) { \
@@ -377,7 +377,9 @@ do { \
377377
#define GOTO_TIER_TWO(EXECUTOR) \
378378
do { \
379379
OPT_STAT_INC(traces_executed); \
380-
next_uop = (EXECUTOR)->trace; \
380+
_PyExecutorObject *_executor = (EXECUTOR); \
381+
tstate->current_executor = (PyObject *)_executor; \
382+
next_uop = _executor->trace; \
381383
assert(next_uop->opcode == _START_EXECUTOR); \
382384
goto enter_tier_two; \
383385
} while (0)
@@ -386,10 +388,11 @@ do { \
386388
#define GOTO_TIER_ONE(TARGET) \
387389
do \
388390
{ \
391+
tstate->current_executor = NULL; \
389392
next_instr = (TARGET); \
393+
assert(tstate->current_executor == NULL); \
390394
OPT_HIST(trace_uop_execution_counter, trace_run_length_hist); \
391395
_PyFrame_SetStackPointer(frame, stack_pointer); \
392-
Py_CLEAR(tstate->previous_executor); \
393396
stack_pointer = _PyFrame_GetStackPointer(frame); \
394397
if (next_instr == NULL) \
395398
{ \

‎Python/executor_cases.c.h

Copy file name to clipboardExpand all lines: Python/executor_cases.c.h
-7Lines changed: 0 additions & 7 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
0 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.