Skip to content

Commit a06ce65

Browse files
committed
Add second API and use IS_PEP523_HOOKED macro, use count in test
1 parent bf9ffc6 commit a06ce65

File tree

7 files changed

+36
-18
lines changed

7 files changed

+36
-18
lines changed

Include/cpython/pystate.h

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -318,7 +318,9 @@ PyAPI_FUNC(_PyFrameEvalFunction) _PyInterpreterState_GetEvalFrameFunc(
318318
PyInterpreterState *interp);
319319
PyAPI_FUNC(void) _PyInterpreterState_SetEvalFrameFunc(
320320
PyInterpreterState *interp,
321-
_PyFrameEvalFunction eval_frame,
321+
_PyFrameEvalFunction eval_frame);
322+
PyAPI_FUNC(void) _PyInterpreterState_SetEvalFrameAllowSpecialization(
323+
PyInterpreterState *interp,
322324
int allow_specialization);
323325
PyAPI_FUNC(int) _PyInterpreterState_IsSpecializationEnabled(
324326
PyInterpreterState *interp);

Lib/test/test_capi/test_misc.py

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2921,12 +2921,10 @@ def func_outer():
29212921

29222922
# With specialization enabled, calls to inner() will dispatch
29232923
# through the installed frame evaluator
2924-
func_calls = [c for c in actual_calls if c == "func"]
2925-
self.assertEqual(len(func_calls), 0)
2924+
self.assertEqual(actual_calls.count("func"), 0)
29262925

29272926
# But the normal interpreter loop still shouldn't be inlining things
2928-
func_outer_calls = [c for c in actual_calls if c == "func_outer"]
2929-
self.assertNotEqual(len(func_outer_calls), 0)
2927+
self.assertNotEqual(actual_calls.count("func_outer"), 0)
29302928

29312929
def test_no_specialization_call(self):
29322930
# Without allow_specialization, ALL calls go through the eval frame.
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
The unstable API _PyInterpreterState_SetEvalFrameFunc takes an additional option to specify if specialization should be allowed. When this option is set to 1 the specializer will turn Python -> Python calls into specialized opcodes and will execute the Python function in the current interpreter loop instead of calling to the frame evaluator.
1+
The unstable API _PyInterpreterState_SetEvalFrameFunc has a companion function _PyInterpreterState_SetEvalFrameAllowSpecialization to specify if specialization should be allowed. When this option is set to 1 the specializer will turn Python -> Python calls into specialized opcodes which the replacement interpreter loop can choose to respect and perform inlined dispatch.

Modules/_testinternalcapi.c

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -929,7 +929,7 @@ static PyObject *
929929
set_eval_frame_default(PyObject *self, PyObject *Py_UNUSED(args))
930930
{
931931
module_state *state = get_module_state(self);
932-
_PyInterpreterState_SetEvalFrameFunc(_PyInterpreterState_GET(), _PyEval_EvalFrameDefault, 0);
932+
_PyInterpreterState_SetEvalFrameFunc(_PyInterpreterState_GET(), _PyEval_EvalFrameDefault);
933933
Py_CLEAR(state->record_list);
934934
Py_RETURN_NONE;
935935
}
@@ -961,7 +961,7 @@ set_eval_frame_record(PyObject *self, PyObject *list)
961961
return NULL;
962962
}
963963
Py_XSETREF(state->record_list, Py_NewRef(list));
964-
_PyInterpreterState_SetEvalFrameFunc(_PyInterpreterState_GET(), record_eval, 0);
964+
_PyInterpreterState_SetEvalFrameFunc(_PyInterpreterState_GET(), record_eval);
965965
Py_RETURN_NONE;
966966
}
967967

@@ -1024,9 +1024,11 @@ set_eval_frame_interp(PyObject *self, PyObject *args)
10241024
return NULL;
10251025
}
10261026
Py_XSETREF(state->record_list, Py_NewRef(list));
1027-
_PyInterpreterState_SetEvalFrameFunc(_PyInterpreterState_GET(), record_eval_interp, 1);
1027+
_PyInterpreterState_SetEvalFrameFunc(_PyInterpreterState_GET(), record_eval_interp);
1028+
_PyInterpreterState_SetEvalFrameAllowSpecialization(_PyInterpreterState_GET(), 1);
10281029
} else {
1029-
_PyInterpreterState_SetEvalFrameFunc(_PyInterpreterState_GET(), Test_EvalFrame, 1);
1030+
_PyInterpreterState_SetEvalFrameFunc(_PyInterpreterState_GET(), Test_EvalFrame);
1031+
_PyInterpreterState_SetEvalFrameAllowSpecialization(_PyInterpreterState_GET(), 1);
10301032
}
10311033

10321034
Py_RETURN_NONE;

Python/ceval_macros.h

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -222,8 +222,7 @@ do { \
222222

223223
#define DISPATCH_INLINED(NEW_FRAME) \
224224
do { \
225-
assert(tstate->interp->eval_frame == NULL || \
226-
tstate->interp->eval_frame_allow_specialization); \
225+
assert(!IS_PEP523_HOOKED(tstate)); \
227226
_PyFrame_SetStackPointer(frame, stack_pointer); \
228227
assert((NEW_FRAME)->previous == frame); \
229228
frame = tstate->current_frame = (NEW_FRAME); \

Python/perf_trampoline.c

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -530,12 +530,12 @@ _PyPerfTrampoline_Init(int activate)
530530
code_watcher_id = -1;
531531
}
532532
if (!activate) {
533-
_PyInterpreterState_SetEvalFrameFunc(tstate->interp, prev_eval_frame, 0);
533+
_PyInterpreterState_SetEvalFrameFunc(tstate->interp, prev_eval_frame);
534534
perf_status = PERF_STATUS_NO_INIT;
535535
}
536536
else if (tstate->interp->eval_frame != py_trampoline_evaluator) {
537537
prev_eval_frame = _PyInterpreterState_GetEvalFrameFunc(tstate->interp);
538-
_PyInterpreterState_SetEvalFrameFunc(tstate->interp, py_trampoline_evaluator, 0);
538+
_PyInterpreterState_SetEvalFrameFunc(tstate->interp, py_trampoline_evaluator);
539539
extra_code_index = _PyEval_RequestCodeExtraIndex(NULL);
540540
if (extra_code_index == -1) {
541541
return -1;
@@ -568,7 +568,7 @@ _PyPerfTrampoline_Fini(void)
568568
}
569569
PyThreadState *tstate = _PyThreadState_GET();
570570
if (tstate->interp->eval_frame == py_trampoline_evaluator) {
571-
_PyInterpreterState_SetEvalFrameFunc(tstate->interp, NULL, 0);
571+
_PyInterpreterState_SetEvalFrameFunc(tstate->interp, NULL);
572572
}
573573
if (perf_status == PERF_STATUS_OK) {
574574
trampoline_api.free_state(trampoline_api.state);

Python/pystate.c

Lines changed: 20 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3010,14 +3010,12 @@ _PyInterpreterState_GetEvalFrameFunc(PyInterpreterState *interp)
30103010

30113011
void
30123012
_PyInterpreterState_SetEvalFrameFunc(PyInterpreterState *interp,
3013-
_PyFrameEvalFunction eval_frame,
3014-
int allow_specialization)
3013+
_PyFrameEvalFunction eval_frame)
30153014
{
30163015
if (eval_frame == _PyEval_EvalFrameDefault) {
30173016
eval_frame = NULL;
30183017
}
30193018
if (eval_frame == interp->eval_frame) {
3020-
interp->eval_frame_allow_specialization = allow_specialization;
30213019
return;
30223020
}
30233021
#ifdef _Py_TIER2
@@ -3028,6 +3026,25 @@ _PyInterpreterState_SetEvalFrameFunc(PyInterpreterState *interp,
30283026
RARE_EVENT_INC(set_eval_frame_func);
30293027
_PyEval_StopTheWorld(interp);
30303028
interp->eval_frame = eval_frame;
3029+
// reset when evaluator is reset
3030+
interp->eval_frame_allow_specialization = 0;
3031+
_PyEval_StartTheWorld(interp);
3032+
}
3033+
3034+
void
3035+
_PyInterpreterState_SetEvalFrameAllowSpecialization(PyInterpreterState *interp,
3036+
int allow_specialization)
3037+
{
3038+
if (allow_specialization == interp->eval_frame_allow_specialization) {
3039+
return;
3040+
}
3041+
#ifdef _Py_TIER2
3042+
if (eval_frame != NULL) {
3043+
_Py_Executors_InvalidateAll(interp, 1);
3044+
}
3045+
#endif
3046+
RARE_EVENT_INC(set_eval_frame_func);
3047+
_PyEval_StopTheWorld(interp);
30313048
interp->eval_frame_allow_specialization = allow_specialization;
30323049
_PyEval_StartTheWorld(interp);
30333050
}

0 commit comments

Comments
 (0)