-
-
Notifications
You must be signed in to change notification settings - Fork 33.5k
gh-141594: A free-threaded JIT #141595
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
gh-141594: A free-threaded JIT #141595
Changes from all commits
6a3bd75
ba9a65a
725894d
1e5713d
f0d4c57
67de7d6
7f0bc57
f05e61c
f78e8c8
e1f1b30
53c5e1d
cc38ee4
f8fefb3
d76a24b
fa99108
fcfed96
ba67ab7
b46385b
46285be
8ce83cd
fa55643
92f3dbf
7240b15
cb87676
f972637
3ef237b
44356d6
b819053
527aac1
b80c02e
4278c9d
d84215d
b08ef60
c2c8fbe
162b1ec
e0890ff
197e9f4
46413cf
b5d6571
a93c26c
97d5f2b
c381903
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,3 @@ | ||
| Add free-threading support to the JIT. The JIT is only enabled on | ||
| single-threaded code in free-threading, and is disabled when multiple | ||
| threads are spawned. Patch by Ken Jin. |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -11,7 +11,7 @@ | |
| #include "pycore_setobject.h" // _PySet_NextEntry() | ||
| #include "pycore_stats.h" | ||
| #include "pycore_weakref.h" // FT_CLEAR_WEAKREFS() | ||
| #include "pycore_optimizer.h" // _PyJit_Tracer_InvalidateDependency | ||
| #include "pycore_optimizer.h" // _Py_Executors_InvalidateDependency | ||
|
|
||
| static const char * | ||
| func_event_name(PyFunction_WatchEvent event) { | ||
|
|
@@ -298,7 +298,7 @@ functions is running. | |
|
|
||
| */ | ||
|
|
||
| #ifndef Py_GIL_DISABLED | ||
| #if _Py_TIER2 | ||
| static inline struct _func_version_cache_item * | ||
| get_cache_item(PyInterpreterState *interp, uint32_t version) | ||
| { | ||
|
|
@@ -315,11 +315,13 @@ _PyFunction_SetVersion(PyFunctionObject *func, uint32_t version) | |
| // This should only be called from MAKE_FUNCTION. No code is specialized | ||
| // based on the version, so we do not need to stop the world to set it. | ||
| func->func_version = version; | ||
| #ifndef Py_GIL_DISABLED | ||
| #if _Py_TIER2 | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This changes the tier 1 with-gil behavior.
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes I double checked. |
||
| PyInterpreterState *interp = _PyInterpreterState_GET(); | ||
| FT_MUTEX_LOCK(&interp->func_state.mutex); | ||
| struct _func_version_cache_item *slot = get_cache_item(interp, version); | ||
| slot->func = func; | ||
| slot->code = func->func_code; | ||
| FT_MUTEX_UNLOCK(&interp->func_state.mutex); | ||
| #endif | ||
| } | ||
|
|
||
|
|
@@ -330,13 +332,15 @@ func_clear_version(PyInterpreterState *interp, PyFunctionObject *func) | |
| // Version was never set or has already been cleared. | ||
| return; | ||
| } | ||
| #ifndef Py_GIL_DISABLED | ||
| #if _Py_TIER2 | ||
| FT_MUTEX_LOCK(&interp->func_state.mutex); | ||
| struct _func_version_cache_item *slot = | ||
| get_cache_item(interp, func->func_version); | ||
| if (slot->func == func) { | ||
| slot->func = NULL; | ||
| // Leave slot->code alone, there may be use for it. | ||
| } | ||
| FT_MUTEX_UNLOCK(&interp->func_state.mutex); | ||
| #endif | ||
| func->func_version = FUNC_VERSION_CLEARED; | ||
| } | ||
|
|
@@ -358,8 +362,9 @@ _PyFunction_ClearVersion(PyFunctionObject *func) | |
| void | ||
| _PyFunction_ClearCodeByVersion(uint32_t version) | ||
| { | ||
| #ifndef Py_GIL_DISABLED | ||
| #if _Py_TIER2 | ||
| PyInterpreterState *interp = _PyInterpreterState_GET(); | ||
| FT_MUTEX_LOCK(&interp->func_state.mutex); | ||
| struct _func_version_cache_item *slot = get_cache_item(interp, version); | ||
| if (slot->code) { | ||
| assert(PyCode_Check(slot->code)); | ||
|
|
@@ -369,15 +374,17 @@ _PyFunction_ClearCodeByVersion(uint32_t version) | |
| slot->func = NULL; | ||
| } | ||
| } | ||
| FT_MUTEX_UNLOCK(&interp->func_state.mutex); | ||
| #endif | ||
| } | ||
|
|
||
| PyFunctionObject * | ||
| _PyFunction_LookupByVersion(uint32_t version, PyObject **p_code) | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is this function really used somewhere?
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. That's strange, you're right, it's not used. It's useful for the optimizer though, so we might want to use it in the future. |
||
| { | ||
| #ifdef Py_GIL_DISABLED | ||
| return NULL; | ||
| #else | ||
| #if _Py_TIER2 | ||
| // This function does not need locking/atomics as it can only be | ||
| // called from the optimizer, which is currently disabled | ||
| // when there are multiple threads. | ||
| PyInterpreterState *interp = _PyInterpreterState_GET(); | ||
| struct _func_version_cache_item *slot = get_cache_item(interp, version); | ||
| if (slot->code) { | ||
|
|
@@ -395,12 +402,18 @@ _PyFunction_LookupByVersion(uint32_t version, PyObject **p_code) | |
| return slot->func; | ||
| } | ||
| return NULL; | ||
| #else | ||
| return NULL; | ||
| #endif | ||
| } | ||
|
|
||
| uint32_t | ||
| _PyFunction_GetVersionForCurrentState(PyFunctionObject *func) | ||
| { | ||
| // This function does not need locking/atomics as it can only be | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We use the function version in specialized instructions, so it is used in tier 1.
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The use in the specializer is protected because it's accessing a stack variable which holds a strong reference to it. I will clarify that the unsnchronized use is only in the optimizer and it's fine there. |
||
| // called from the specializing interpreter or optimizer. | ||
| // The specializing interpreter holds a strong reference to the function. | ||
| // The optimizer is currently disabled when there are multiple threads. | ||
| return func->func_version; | ||
| } | ||
|
|
||
|
|
@@ -1153,7 +1166,6 @@ func_dealloc(PyObject *self) | |
| } | ||
| #if _Py_TIER2 | ||
| _Py_Executors_InvalidateDependency(_PyInterpreterState_GET(), self, 1); | ||
| _PyJit_Tracer_InvalidateDependency(_PyThreadState_GET(), self); | ||
Fidget-Spinner marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| #endif | ||
| _PyObject_GC_UNTRACK(op); | ||
| FT_CLEAR_WEAKREFS(self, op->func_weakreflist); | ||
|
|
||
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This can be reverted too.