gh-115999: Specialize CALL_KW in free-threaded builds#127713
Merged
Uh oh!
There was an error while loading. Please reload this page.
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
The
CALL_KWfamily was already thread-safe. Only one change to fix a bug in_PY_FRAME_KWwas needed:_PY_FRAME_KWpushes 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 becausePyStackRef_NULLandNULLare the same value, so thePyStackRef_XCLOSE()in the error handler ignores it. In the free-threaded build the values are not the same, causingPyStackRef_XCLOSE()to decref a null pointer.Single-threaded performance
Performance looks neutral on both builds. I think this is still worth merging to keep the two builds consistent, however.
Scalability
Scalability looks roughly unchanged:
Thread safety
The specialized instructions are composed of the follow uops, whose thread-safety is documented below.
_CALL_KW_NON_PY- Tuples are immutable; it's fine to read their size non-atomically._CHECK_FUNCTION_VERSION_KW- This uop guards that the top of the stack is a function and that its version matches the version stored in the inline cache. Instructions assume that if the guard passes, the version, and any properties verified by the version, will not change for the remainder of the instruction execution, assuming there are no escaping calls in between the guard and the code that relies on the guard. This property is preserved in free-threaded builds: the world is stopped whenever a function's version changes._CHECK_IS_NOT_PY_CALLABLE_KW- Performs type checks._CHECK_METHOD_VERSION_KW- This loads a function from a PyMethodObject and guards that its version matches what is stored in the cache. PyMethodObjects are immutable; their fields can be accessed non-atomically. The thread safety of function version guards was already documented above._CHECK_PEP_523- Thread safety was addressed as part ofCALL._CHECK_PERIODIC- Thread safety was previously addressed as part of the 3.13 release._EXPAND_METHOD_KW- Only loads from PyMethodObjects and performs type checks._PUSH_FRAME- Only manipulateststate->current_frameand fields that are not read by other threads._PY_FRAME_KW- Loads immutable fields (tuple size, code flags) or fields that require stopping the world to modify (function code) and uses thread-safe APIs._SAVE_RETURN_OFFSET- Stores only to the frame's return_offset which is not read by other threads.Specialization only looks at properties that require stopping the world to update (i.e. instance type,
eval_frame,func_code, and the function version) and contains no escaping calls.--disable-gilbuilds #115999