Skip to content

Commit

Permalink
gh-115999: Specialize CALL_KW in free-threaded builds (#127713)
Browse files Browse the repository at this point in the history
* Enable specialization of CALL_KW

* Fix bug pushing frame in _PY_FRAME_KW

`_PY_FRAME_KW` pushes a pointer to the new frame onto the stack for
consumption by the next uop. When pushing the frame fails, we do not
want to push the result, `NULL`, to the stack because it is not
a valid stackref. This works in the default build because `PyStackRef_NULL`
 and `NULL` are the same value, so the `PyStackRef_XCLOSE()` in the error
handler ignores it. In the free-threaded build the values are not the same;
`PyStackRef_XCLOSE()` will attempt to decref a null pointer.
  • Loading branch information
mpage authored Dec 11, 2024
1 parent e8f4e27 commit c84928e
Show file tree
Hide file tree
Showing 4 changed files with 26 additions and 35 deletions.
9 changes: 5 additions & 4 deletions Python/bytecodes.c
Original file line number Diff line number Diff line change
Expand Up @@ -4311,17 +4311,18 @@ dummy_func(
assert(Py_TYPE(callable_o) == &PyFunction_Type);
int code_flags = ((PyCodeObject*)PyFunction_GET_CODE(callable_o))->co_flags;
PyObject *locals = code_flags & CO_OPTIMIZED ? NULL : Py_NewRef(PyFunction_GET_GLOBALS(callable_o));
new_frame = _PyEvalFramePushAndInit(
_PyInterpreterFrame *temp = _PyEvalFramePushAndInit(
tstate, callable[0], locals,
args, positional_args, kwnames_o, frame
);
PyStackRef_CLOSE(kwnames);
// The frame has stolen all the arguments from the stack,
// so there is no need to clean them up.
SYNC_SP();
if (new_frame == NULL) {
if (temp == NULL) {
ERROR_NO_POP();
}
new_frame = temp;
}

op(_CHECK_FUNCTION_VERSION_KW, (func_version/2, callable[1], self_or_null[1], unused[oparg], kwnames -- callable[1], self_or_null[1], unused[oparg], kwnames)) {
Expand Down Expand Up @@ -4372,15 +4373,15 @@ dummy_func(
_PUSH_FRAME;

specializing op(_SPECIALIZE_CALL_KW, (counter/1, callable[1], self_or_null[1], args[oparg], kwnames -- callable[1], self_or_null[1], args[oparg], kwnames)) {
#if ENABLE_SPECIALIZATION
#if ENABLE_SPECIALIZATION_FT
if (ADAPTIVE_COUNTER_TRIGGERS(counter)) {
next_instr = this_instr;
_Py_Specialize_CallKw(callable[0], next_instr, oparg + !PyStackRef_IsNull(self_or_null[0]));
DISPATCH_SAME_OPARG();
}
OPCODE_DEFERRED_INC(CALL_KW);
ADVANCE_ADAPTIVE_COUNTER(this_instr[1].counter);
#endif /* ENABLE_SPECIALIZATION */
#endif /* ENABLE_SPECIALIZATION_FT */
}

macro(CALL_KW) =
Expand Down
11 changes: 7 additions & 4 deletions Python/executor_cases.c.h

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

24 changes: 10 additions & 14 deletions Python/generated_cases.c.h

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

17 changes: 4 additions & 13 deletions Python/specialize.c
Original file line number Diff line number Diff line change
Expand Up @@ -2107,7 +2107,7 @@ specialize_py_call_kw(PyFunctionObject *func, _Py_CODEUNIT *instr, int nargs,
return -1;
}
write_u32(cache->func_version, version);
instr->op.code = bound_method ? CALL_KW_BOUND_METHOD : CALL_KW_PY;
specialize(instr, bound_method ? CALL_KW_BOUND_METHOD : CALL_KW_PY);
return 0;
}

Expand Down Expand Up @@ -2202,10 +2202,9 @@ _Py_Specialize_CallKw(_PyStackRef callable_st, _Py_CODEUNIT *instr, int nargs)
{
PyObject *callable = PyStackRef_AsPyObjectBorrow(callable_st);

assert(ENABLE_SPECIALIZATION);
assert(ENABLE_SPECIALIZATION_FT);
assert(_PyOpcode_Caches[CALL_KW] == INLINE_CACHE_ENTRIES_CALL_KW);
assert(_Py_OPCODE(*instr) != INSTRUMENTED_CALL_KW);
_PyCallCache *cache = (_PyCallCache *)(instr + 1);
int fail;
if (PyFunction_Check(callable)) {
fail = specialize_py_call_kw((PyFunctionObject *)callable, instr, nargs, false);
Expand All @@ -2221,19 +2220,11 @@ _Py_Specialize_CallKw(_PyStackRef callable_st, _Py_CODEUNIT *instr, int nargs)
}
}
else {
instr->op.code = CALL_KW_NON_PY;
specialize(instr, CALL_KW_NON_PY);
fail = 0;
}
if (fail) {
STAT_INC(CALL, failure);
assert(!PyErr_Occurred());
instr->op.code = CALL_KW;
cache->counter = adaptive_counter_backoff(cache->counter);
}
else {
STAT_INC(CALL, success);
assert(!PyErr_Occurred());
cache->counter = adaptive_counter_cooldown();
unspecialize(instr);
}
}

Expand Down

0 comments on commit c84928e

Please sign in to comment.