Skip to content

Commit d84215d

Browse files
Re-enable the type/function reverse cache
1 parent 4278c9d commit d84215d

File tree

4 files changed

+29
-22
lines changed

4 files changed

+29
-22
lines changed

Lib/test/test_capi/test_opt.py

Lines changed: 0 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,6 @@ def iter_opnames(ex):
6464
def get_opnames(ex):
6565
return list(iter_opnames(ex))
6666

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

6968
@requires_jit_enabled
7069
class TestExecutorInvalidation(unittest.TestCase):
@@ -1186,7 +1185,6 @@ def test_modified_local_is_seen_by_optimized_code(self):
11861185
self.assertIs(type(s), float)
11871186
self.assertEqual(s, 1024.0)
11881187

1189-
@unittest.skipIf(Py_GIL_DISABLED, FT_TEST_DISABLED_REVERSE_TYPE_CACHE)
11901188
def test_guard_type_version_removed(self):
11911189
def thing(a):
11921190
x = 0
@@ -1205,7 +1203,6 @@ class Foo:
12051203
guard_type_version_count = opnames.count("_GUARD_TYPE_VERSION")
12061204
self.assertEqual(guard_type_version_count, 1)
12071205

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

1235-
@unittest.skipIf(Py_GIL_DISABLED, FT_TEST_DISABLED_REVERSE_TYPE_CACHE)
12361232
def test_guard_type_version_removed_invalidation(self):
12371233

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

1266-
@unittest.skipIf(Py_GIL_DISABLED, FT_TEST_DISABLED_REVERSE_TYPE_CACHE)
12671262
def test_guard_type_version_removed_escaping(self):
12681263

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

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

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

2370-
@unittest.skipIf(Py_GIL_DISABLED, FT_TEST_DISABLED_REVERSE_TYPE_CACHE)
23712363
def test_cached_attributes(self):
23722364
class C:
23732365
A = 1
@@ -2506,7 +2498,6 @@ def testfunc(n):
25062498

25072499
self.assertIn("_POP_TOP_NOP", uops)
25082500

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

25212512
self.assertIn("_POP_TOP_INT", uops)
25222513

2523-
@unittest.skipIf(Py_GIL_DISABLED, "FT build immortalizes constants")
25242514
def test_pop_top_specialize_float(self):
25252515
def testfunc(n):
25262516
for _ in range(n):

Objects/funcobject.c

Lines changed: 17 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -298,7 +298,7 @@ functions is running.
298298
299299
*/
300300

301-
#ifndef Py_GIL_DISABLED
301+
#if _Py_TIER2
302302
static inline struct _func_version_cache_item *
303303
get_cache_item(PyInterpreterState *interp, uint32_t version)
304304
{
@@ -315,11 +315,13 @@ _PyFunction_SetVersion(PyFunctionObject *func, uint32_t version)
315315
// This should only be called from MAKE_FUNCTION. No code is specialized
316316
// based on the version, so we do not need to stop the world to set it.
317317
func->func_version = version;
318-
#ifndef Py_GIL_DISABLED
318+
#if _Py_TIER2
319319
PyInterpreterState *interp = _PyInterpreterState_GET();
320+
FT_MUTEX_LOCK(&interp->func_state.mutex);
320321
struct _func_version_cache_item *slot = get_cache_item(interp, version);
321322
slot->func = func;
322323
slot->code = func->func_code;
324+
FT_MUTEX_UNLOCK(&interp->func_state.mutex);
323325
#endif
324326
}
325327

@@ -330,13 +332,15 @@ func_clear_version(PyInterpreterState *interp, PyFunctionObject *func)
330332
// Version was never set or has already been cleared.
331333
return;
332334
}
333-
#ifndef Py_GIL_DISABLED
335+
#if _Py_TIER2
336+
FT_MUTEX_LOCK(&interp->func_state.mutex);
334337
struct _func_version_cache_item *slot =
335338
get_cache_item(interp, func->func_version);
336339
if (slot->func == func) {
337340
slot->func = NULL;
338341
// Leave slot->code alone, there may be use for it.
339342
}
343+
FT_MUTEX_UNLOCK(&interp->func_state.mutex);
340344
#endif
341345
func->func_version = FUNC_VERSION_CLEARED;
342346
}
@@ -358,8 +362,9 @@ _PyFunction_ClearVersion(PyFunctionObject *func)
358362
void
359363
_PyFunction_ClearCodeByVersion(uint32_t version)
360364
{
361-
#ifndef Py_GIL_DISABLED
365+
#if _Py_TIER2
362366
PyInterpreterState *interp = _PyInterpreterState_GET();
367+
FT_MUTEX_LOCK(&interp->func_state.mutex);
363368
struct _func_version_cache_item *slot = get_cache_item(interp, version);
364369
if (slot->code) {
365370
assert(PyCode_Check(slot->code));
@@ -369,15 +374,17 @@ _PyFunction_ClearCodeByVersion(uint32_t version)
369374
slot->func = NULL;
370375
}
371376
}
377+
FT_MUTEX_UNLOCK(&interp->func_state.mutex);
372378
#endif
373379
}
374380

375381
PyFunctionObject *
376382
_PyFunction_LookupByVersion(uint32_t version, PyObject **p_code)
377383
{
378-
#ifdef Py_GIL_DISABLED
379-
return NULL;
380-
#else
384+
#if _Py_TIER2
385+
// This function does not need locking/atomics as it can only be
386+
// called from the optimizer, which is currently disabled
387+
// when there are multiple threads.
381388
PyInterpreterState *interp = _PyInterpreterState_GET();
382389
struct _func_version_cache_item *slot = get_cache_item(interp, version);
383390
if (slot->code) {
@@ -401,6 +408,9 @@ _PyFunction_LookupByVersion(uint32_t version, PyObject **p_code)
401408
uint32_t
402409
_PyFunction_GetVersionForCurrentState(PyFunctionObject *func)
403410
{
411+
// This function does not need locking/atomics as it can only be
412+
// called from the optimizer, which is currently disabled
413+
// when there are multiple threads.
404414
return func->func_version;
405415
}
406416

Objects/typeobject.c

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1148,15 +1148,18 @@ static void
11481148
set_version_unlocked(PyTypeObject *tp, unsigned int version)
11491149
{
11501150
assert(version == 0 || (tp->tp_versions_used != _Py_ATTR_CACHE_UNUSED));
1151-
#ifndef Py_GIL_DISABLED
1151+
#if _Py_TIER2
11521152
PyInterpreterState *interp = _PyInterpreterState_GET();
1153+
BEGIN_TYPE_LOCK();
11531154
// lookup the old version and set to null
11541155
if (tp->tp_version_tag != 0) {
11551156
PyTypeObject **slot =
11561157
interp->types.type_version_cache
11571158
+ (tp->tp_version_tag % TYPE_VERSION_CACHE_SIZE);
11581159
*slot = NULL;
11591160
}
1161+
#endif
1162+
#ifndef Py_GIL_DISABLED
11601163
if (version) {
11611164
tp->tp_versions_used++;
11621165
}
@@ -1166,13 +1169,14 @@ set_version_unlocked(PyTypeObject *tp, unsigned int version)
11661169
}
11671170
#endif
11681171
FT_ATOMIC_STORE_UINT_RELAXED(tp->tp_version_tag, version);
1169-
#ifndef Py_GIL_DISABLED
1172+
#if _Py_TIER2
11701173
if (version != 0) {
11711174
PyTypeObject **slot =
11721175
interp->types.type_version_cache
11731176
+ (version % TYPE_VERSION_CACHE_SIZE);
11741177
*slot = tp;
11751178
}
1179+
END_TYPE_LOCK();
11761180
#endif
11771181
}
11781182

@@ -1357,9 +1361,12 @@ _PyType_SetVersion(PyTypeObject *tp, unsigned int version)
13571361
PyTypeObject *
13581362
_PyType_LookupByVersion(unsigned int version)
13591363
{
1360-
#ifdef Py_GIL_DISABLED
1364+
#ifndef _Py_TIER2
13611365
return NULL;
13621366
#else
1367+
// This function does not need locking/atomics as it can only be
1368+
// called from the optimizer, which is currently disabled
1369+
// when there are multiple threads.
13631370
PyInterpreterState *interp = _PyInterpreterState_GET();
13641371
PyTypeObject **slot =
13651372
interp->types.type_version_cache

Python/optimizer.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1655,12 +1655,12 @@ jit_tracer_invalidate_dependency(PyThreadState *tstate, void *obj)
16551655
_Py_BloomFilter_Init(&obj_filter);
16561656
_Py_BloomFilter_Add(&obj_filter, obj);
16571657
_PyThreadStateImpl *_tstate = (_PyThreadStateImpl *)tstate;
1658-
PyMutex_Lock(&_tstate->jit_tracer_state.lock);
1658+
FT_MUTEX_LOCK(&_tstate->jit_tracer_state.lock);
16591659
if (bloom_filter_may_contain(&_tstate->jit_tracer_state.prev_state.dependencies, &obj_filter))
16601660
{
16611661
FT_ATOMIC_STORE_UINT8(_tstate->jit_tracer_state.prev_state.dependencies_still_valid, 0);
16621662
}
1663-
PyMutex_Unlock(&_tstate->jit_tracer_state.lock);
1663+
FT_MUTEX_UNLOCK(&_tstate->jit_tracer_state.lock);
16641664
}
16651665

16661666
void

0 commit comments

Comments
 (0)