FazBrowse GitHub Viewer | Trending |
URL:
| Home
Tools: [Download Repo ZIP]   [Original HTTPS Page]

gh-141594: A free-threaded JIT (Part 1) by Fidget-Spinner · Pull Request #141595 · python/cpython · GitHub

Repository navigation

Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
62 commits
Select commit Hold shift + click to select a range
6a3bd75
Move all JIT fields to thread state
Fidget-Spinner Nov 13, 2025
ba9a65a
fix a bug with traversing states
Fidget-Spinner Nov 13, 2025
725894d
fix JIT invalidation mechanism for FT
Fidget-Spinner Nov 14, 2025
1e5713d
fix re-entrant finalizers
Fidget-Spinner Nov 14, 2025
f0d4c57
Re-enable the JIT
Fidget-Spinner Nov 14, 2025
67de7d6
cleanup a little
Fidget-Spinner Nov 14, 2025
7f0bc57
fix weird GC bugs
Fidget-Spinner Nov 14, 2025
f05e61c
re-enable jit on some stuff
Fidget-Spinner Nov 14, 2025
f78e8c8
fix test, more locks!
Fidget-Spinner Nov 14, 2025
e1f1b30
fix JIT builds
Fidget-Spinner Nov 14, 2025
53c5e1d
only clear at end
Fidget-Spinner Nov 14, 2025
cc38ee4
remove locks in JIT code
Fidget-Spinner Nov 14, 2025
f8fefb3
fix a few bugs
Fidget-Spinner Nov 14, 2025
d76a24b
Improve tracer thread safety
Fidget-Spinner Nov 15, 2025
fa99108
set immortal before GC
Fidget-Spinner Nov 15, 2025
fcfed96
📜🤖 Added by blurb_it.
blurb-it[bot] Nov 15, 2025
ba67ab7
fix default builkd
Fidget-Spinner Nov 15, 2025
b46385b
Merge branch 'jit_ft' of github.com:Fidget-Spinner/cpython into jit_ft
Fidget-Spinner Nov 15, 2025
46285be
Merge remote-tracking branch 'upstream/main' into jit_ft
Fidget-Spinner Nov 15, 2025
8ce83cd
fix comment
Fidget-Spinner Nov 15, 2025
fa55643
lint
Fidget-Spinner Nov 15, 2025
92f3dbf
fix nojit builds
Fidget-Spinner Nov 15, 2025
7240b15
Remove heavywieght locking--not needed
Fidget-Spinner Nov 15, 2025
cb87676
address review, fix bug
Fidget-Spinner Nov 15, 2025
f972637
GC executors
Fidget-Spinner Nov 15, 2025
3ef237b
Change the name
Fidget-Spinner Nov 16, 2025
44356d6
Address review, remove more refcounting
Fidget-Spinner Nov 16, 2025
b819053
Fix TSAN races
Fidget-Spinner Nov 16, 2025
527aac1
Remove atomics from _CHECK_VALIDITY
Fidget-Spinner Nov 16, 2025
b80c02e
fix comment
Fidget-Spinner Nov 16, 2025
4278c9d
fix typo
Fidget-Spinner Nov 16, 2025
d84215d
Re-enable the type/function reverse cache
Fidget-Spinner Nov 16, 2025
b08ef60
allow allow deferred things to be borrrowed too
Fidget-Spinner Nov 16, 2025
c2c8fbe
fix warnings
Fidget-Spinner Nov 16, 2025
162b1ec
skip bad tests
Fidget-Spinner Nov 16, 2025
e0890ff
skip bad tests
Fidget-Spinner Nov 16, 2025
197e9f4
reduce diff
Fidget-Spinner Nov 17, 2025
46413cf
Reduce diff to minmal
Fidget-Spinner Nov 17, 2025
b5d6571
reduce diff further
Fidget-Spinner Nov 17, 2025
a93c26c
Reduce diff to near minimal
Fidget-Spinner Nov 17, 2025
97d5f2b
typo fix
Fidget-Spinner Nov 17, 2025
c381903
Address review
Fidget-Spinner Nov 17, 2025
0d6751a
Merge remote-tracking branch 'upstream/main' into jit_ft
Fidget-Spinner Dec 16, 2025
8190a5c
Merge remote-tracking branch 'upstream/main' into jit_ft
Fidget-Spinner Dec 16, 2025
973e961
disable DCE pass for now
Fidget-Spinner Dec 17, 2025
2f39aa8
Merge remote-tracking branch 'upstream/main' into jit_ft
Fidget-Spinner Dec 17, 2025
e08c434
comment out object locks
Fidget-Spinner Dec 18, 2025
00ae7b5
enable GIL on JIT
Fidget-Spinner Dec 18, 2025
4d9ffa1
Merge remote-tracking branch 'upstream/main' into jit_ft
Fidget-Spinner Dec 28, 2025
a1ca3a7
fix upstream merges
Fidget-Spinner Dec 28, 2025
cdbbbe3
minor fixups
Fidget-Spinner Dec 28, 2025
0678505
Fix a bug
Fidget-Spinner Dec 28, 2025
dc1e25c
Merge remote-tracking branch 'upstream/main' into jit_ft
Fidget-Spinner Jan 5, 2026
c2416a6
Merge remote-tracking branch 'upstream/main' into jit_ft
Fidget-Spinner Jan 7, 2026
65b6dd0
Address review comments
Fidget-Spinner Jan 7, 2026
7d97437
reduce diff
Fidget-Spinner Jan 7, 2026
1a4f2d0
fix build
Fidget-Spinner Jan 7, 2026
2494965
Add a test
Fidget-Spinner Jan 7, 2026
aff296a
reduce diff
Fidget-Spinner Jan 7, 2026
8df9aed
Merge remote-tracking branch 'upstream/main' into jit_ft
Fidget-Spinner Jan 9, 2026
fb56baa
check env var when re-enabling
Fidget-Spinner Jan 10, 2026
5d987e8
fix warnings
Fidget-Spinner Jan 10, 2026
File filter

Filter by extension

Filter by extension .c  (3) .py  (1) All 2 file types selected
Viewed files
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Unified
Split
Hide whitespace
Diff view
Unified
Split
Hide whitespace
Prev Previous commit
Next Next commit
Re-enable the type/function reverse cache
  • Loading branch information
Fidget-Spinner committed Nov 16, 2025
commit d84215d375854e24574a1e47d4dd634f6beddcb1
10 changes: 0 additions & 10 deletions Lib/test/test_capi/test_opt.py
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode characters
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,6 @@ def iter_opnames(ex):
def get_opnames(ex):
return list(iter_opnames(ex))

FT_TEST_DISABLED_REVERSE_TYPE_CACHE = "FT build reverse type cache is disabled for now"

@requires_jit_enabled
class TestExecutorInvalidation(unittest.TestCase):
Expand Down Expand Up @@ -1186,7 +1185,6 @@ def test_modified_local_is_seen_by_optimized_code(self):
self.assertIs(type(s), float)
self.assertEqual(s, 1024.0)

@unittest.skipIf(Py_GIL_DISABLED, FT_TEST_DISABLED_REVERSE_TYPE_CACHE)
def test_guard_type_version_removed(self):
def thing(a):
x = 0
Expand All @@ -1205,7 +1203,6 @@ class Foo:
guard_type_version_count = opnames.count("_GUARD_TYPE_VERSION")
self.assertEqual(guard_type_version_count, 1)

@unittest.skipIf(Py_GIL_DISABLED, FT_TEST_DISABLED_REVERSE_TYPE_CACHE)
def test_guard_type_version_removed_inlined(self):
"""
Verify that the guard type version if we have an inlined function
Expand All @@ -1232,7 +1229,6 @@ class Foo:
guard_type_version_count = opnames.count("_GUARD_TYPE_VERSION")
self.assertEqual(guard_type_version_count, 1)

@unittest.skipIf(Py_GIL_DISABLED, FT_TEST_DISABLED_REVERSE_TYPE_CACHE)
def test_guard_type_version_removed_invalidation(self):

def thing(a):
Expand Down Expand Up @@ -1263,7 +1259,6 @@ class Bar:
self.assertEqual(opnames[:load_attr_top].count("_GUARD_TYPE_VERSION"), 1)
self.assertEqual(opnames[call:load_attr_bottom].count("_CHECK_VALIDITY"), 2)

@unittest.skipIf(Py_GIL_DISABLED, FT_TEST_DISABLED_REVERSE_TYPE_CACHE)
def test_guard_type_version_removed_escaping(self):

def thing(a):
Expand All @@ -1287,7 +1282,6 @@ class Foo:
self.assertEqual(opnames[:load_attr_top].count("_GUARD_TYPE_VERSION"), 1)
self.assertEqual(opnames[call:load_attr_bottom].count("_CHECK_VALIDITY"), 2)

@unittest.skipIf(Py_GIL_DISABLED, "FT build only specializes on deferred methods/functions/classes")
def test_guard_type_version_executor_invalidated(self):
"""
Verify that the executor is invalided on a type change.
Expand Down Expand Up @@ -2334,7 +2328,6 @@ def testfunc(n):
self.assertNotIn("_TO_BOOL_BOOL", uops)
self.assertIn("_GUARD_IS_TRUE_POP", uops)

@unittest.skipIf(Py_GIL_DISABLED, "FT build only specializes on deferred methods/functions/classes")
def test_set_type_version_sets_type(self):
class C:
A = 1
Expand Down Expand Up @@ -2367,7 +2360,6 @@ def testfunc(n):
self.assertNotIn("_LOAD_SMALL_INT", uops)
self.assertIn("_LOAD_CONST_INLINE_BORROW", uops)

@unittest.skipIf(Py_GIL_DISABLED, FT_TEST_DISABLED_REVERSE_TYPE_CACHE)
def test_cached_attributes(self):
class C:
A = 1
Expand Down Expand Up @@ -2506,7 +2498,6 @@ def testfunc(n):

self.assertIn("_POP_TOP_NOP", uops)

@unittest.skipIf(Py_GIL_DISABLED, "FT build immortalizes constants")
def test_pop_top_specialize_int(self):
def testfunc(n):
for _ in range(n):
Expand All @@ -2520,7 +2511,6 @@ def testfunc(n):

self.assertIn("_POP_TOP_INT", uops)

@unittest.skipIf(Py_GIL_DISABLED, "FT build immortalizes constants")
def test_pop_top_specialize_float(self):
def testfunc(n):
for _ in range(n):
Expand Down
24 changes: 17 additions & 7 deletions Objects/funcobject.c
Show comments Show annotations View file Open in desktop
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode characters
Original file line number Diff line number Diff line change
Expand Up @@ -298,7 +298,7 @@

*/

#ifndef Py_GIL_DISABLED
#if _Py_TIER2
static inline struct _func_version_cache_item *
get_cache_item(PyInterpreterState *interp, uint32_t version)
{
Expand All @@ -315,11 +315,13 @@
// 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

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Choose a reason Spam Abuse Off Topic Outdated Duplicate Resolved Low Quality

This changes the tier 1 with-gil behavior.
I don't see why we would need the cache in tier 1, but can you double check?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Choose a reason Spam Abuse Off Topic Outdated Duplicate Resolved Low Quality

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
}

Expand All @@ -330,13 +332,15 @@
// 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;
}
Expand All @@ -358,8 +362,9 @@
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));
Expand All @@ -369,15 +374,17 @@
slot->func = NULL;
}
}
FT_MUTEX_UNLOCK(&interp->func_state.mutex);
#endif
}

PyFunctionObject *
_PyFunction_LookupByVersion(uint32_t version, PyObject **p_code)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Choose a reason Spam Abuse Off Topic Outdated Duplicate Resolved Low Quality

Is this function really used somewhere?
Maybe we should remove it on main in a separate PR?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Choose a reason Spam Abuse Off Topic Outdated Duplicate Resolved Low Quality

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
Comment thread
Fidget-Spinner marked this conversation as resolved.
Resolved
// 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) {
Expand All @@ -396,11 +403,14 @@
}
return NULL;
#endif
}

Check warning on line 406 in Objects/funcobject.c

View workflow job for this annotation

GitHub Actions / Ubuntu / build and test (ubuntu-24.04)

control reaches end of non-void function [-Wreturn-type]

Check warning on line 406 in Objects/funcobject.c

View workflow job for this annotation

GitHub Actions / Ubuntu / build and test (ubuntu-24.04-arm)

control reaches end of non-void function [-Wreturn-type]

Check warning on line 406 in Objects/funcobject.c

View workflow job for this annotation

GitHub Actions / Cross build Linux

control reaches end of non-void function [-Wreturn-type]

Check warning on line 406 in Objects/funcobject.c

View workflow job for this annotation

GitHub Actions / Hypothesis tests on Ubuntu

control reaches end of non-void function [-Wreturn-type]

Check warning on line 406 in Objects/funcobject.c

View workflow job for this annotation

GitHub Actions / Ubuntu (bolt) / build and test (ubuntu-24.04)

control reaches end of non-void function [-Wreturn-type]

Check warning on line 406 in Objects/funcobject.c

View workflow job for this annotation

GitHub Actions / Ubuntu (free-threading) / build and test (ubuntu-24.04-arm)

control reaches end of non-void function [-Wreturn-type]

Check warning on line 406 in Objects/funcobject.c

View workflow job for this annotation

GitHub Actions / Address sanitizer (ubuntu-24.04)

control reaches end of non-void function [-Wreturn-type]

Check warning on line 406 in Objects/funcobject.c

View workflow job for this annotation

GitHub Actions / Windows / Build and test (x64)

'_PyFunction_LookupByVersion': must return a value [D:\a\cpython\cpython\PCbuild\pythoncore.vcxproj]

Check warning on line 406 in Objects/funcobject.c

View workflow job for this annotation

GitHub Actions / Windows / Build and test (x64)

'_PyFunction_LookupByVersion': must return a value [D:\a\cpython\cpython\PCbuild\_freeze_module.vcxproj]

Check warning on line 406 in Objects/funcobject.c

View workflow job for this annotation

GitHub Actions / Windows (free-threading) / Build and test (x64)

'_PyFunction_LookupByVersion': must return a value [D:\a\cpython\cpython\PCbuild\pythoncore.vcxproj]

Check warning on line 406 in Objects/funcobject.c

View workflow job for this annotation

GitHub Actions / Windows (free-threading) / Build and test (x64)

'_PyFunction_LookupByVersion': must return a value [D:\a\cpython\cpython\PCbuild\_freeze_module.vcxproj]

Check warning on line 406 in Objects/funcobject.c

View workflow job for this annotation

GitHub Actions / Windows (free-threading) / Build and test (arm64)

'_PyFunction_LookupByVersion': must return a value [C:\a\cpython\cpython\PCbuild\pythoncore.vcxproj]

Check warning on line 406 in Objects/funcobject.c

View workflow job for this annotation

GitHub Actions / Windows (free-threading) / Build and test (arm64)

'_PyFunction_LookupByVersion': must return a value [C:\a\cpython\cpython\PCbuild\_freeze_module.vcxproj]

Check warning on line 406 in Objects/funcobject.c

View workflow job for this annotation

GitHub Actions / Windows / Build and test (arm64)

'_PyFunction_LookupByVersion': must return a value [C:\a\cpython\cpython\PCbuild\pythoncore.vcxproj]

Check warning on line 406 in Objects/funcobject.c

View workflow job for this annotation

GitHub Actions / Windows / Build and test (arm64)

'_PyFunction_LookupByVersion': must return a value [C:\a\cpython\cpython\PCbuild\_freeze_module.vcxproj]

Check warning on line 406 in Objects/funcobject.c

View workflow job for this annotation

GitHub Actions / Ubuntu (free-threading) / build and test (ubuntu-24.04)

control reaches end of non-void function [-Wreturn-type]

uint32_t
_PyFunction_GetVersionForCurrentState(PyFunctionObject *func)
{
// This function does not need locking/atomics as it can only be

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Choose a reason Spam Abuse Off Topic Outdated Duplicate Resolved Low Quality

We use the function version in specialized instructions, so it is used in tier 1.
I'm surprised this isn't already synchronized.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Choose a reason Spam Abuse Off Topic Outdated Duplicate Resolved Low Quality

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 optimizer, which is currently disabled
// when there are multiple threads.
return func->func_version;
}

Expand Down
13 changes: 10 additions & 3 deletions Objects/typeobject.c
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode characters
Original file line number Diff line number Diff line change
Expand Up @@ -1148,15 +1148,18 @@ static void
set_version_unlocked(PyTypeObject *tp, unsigned int version)
{
assert(version == 0 || (tp->tp_versions_used != _Py_ATTR_CACHE_UNUSED));
#ifndef Py_GIL_DISABLED
#if _Py_TIER2
Comment thread
Fidget-Spinner marked this conversation as resolved.
Resolved
Comment thread
Fidget-Spinner marked this conversation as resolved.
Resolved
PyInterpreterState *interp = _PyInterpreterState_GET();
BEGIN_TYPE_LOCK();
// lookup the old version and set to null
if (tp->tp_version_tag != 0) {
PyTypeObject **slot =
interp->types.type_version_cache
+ (tp->tp_version_tag % TYPE_VERSION_CACHE_SIZE);
*slot = NULL;
}
#endif
#ifndef Py_GIL_DISABLED
if (version) {
tp->tp_versions_used++;
}
Expand All @@ -1166,13 +1169,14 @@ set_version_unlocked(PyTypeObject *tp, unsigned int version)
}
#endif
FT_ATOMIC_STORE_UINT_RELAXED(tp->tp_version_tag, version);
#ifndef Py_GIL_DISABLED
#if _Py_TIER2
if (version != 0) {
PyTypeObject **slot =
interp->types.type_version_cache
+ (version % TYPE_VERSION_CACHE_SIZE);
*slot = tp;
}
END_TYPE_LOCK();
#endif
}

Expand Down Expand Up @@ -1357,9 +1361,12 @@ _PyType_SetVersion(PyTypeObject *tp, unsigned int version)
PyTypeObject *
_PyType_LookupByVersion(unsigned int version)
{
#ifdef Py_GIL_DISABLED
#ifndef _Py_TIER2
return NULL;
#else
// 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();
PyTypeObject **slot =
interp->types.type_version_cache
Expand Down
4 changes: 2 additions & 2 deletions Python/optimizer.c
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode characters
Original file line number Diff line number Diff line change
Expand Up @@ -1655,12 +1655,12 @@ jit_tracer_invalidate_dependency(PyThreadState *tstate, void *obj)
_Py_BloomFilter_Init(&obj_filter);
_Py_BloomFilter_Add(&obj_filter, obj);
_PyThreadStateImpl *_tstate = (_PyThreadStateImpl *)tstate;
PyMutex_Lock(&_tstate->jit_tracer_state.lock);
FT_MUTEX_LOCK(&_tstate->jit_tracer_state.lock);
if (bloom_filter_may_contain(&_tstate->jit_tracer_state.prev_state.dependencies, &obj_filter))
{
FT_ATOMIC_STORE_UINT8(_tstate->jit_tracer_state.prev_state.dependencies_still_valid, 0);
}
PyMutex_Unlock(&_tstate->jit_tracer_state.lock);
FT_MUTEX_UNLOCK(&_tstate->jit_tracer_state.lock);
}

void
Expand Down
Loading

Back | FazBrowse Home | New Git URL