Skip to content

Commit bb23e4b

Browse files
committed
gh-115999: Enable BINARY_SUBSCR_GETITEM for free-threaded build
1 parent 7015485 commit bb23e4b

File tree

9 files changed

+108
-31
lines changed

9 files changed

+108
-31
lines changed

Include/internal/pycore_opcode_metadata.h

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Include/internal/pycore_typeobject.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -278,6 +278,9 @@ typedef int (*_py_validate_type)(PyTypeObject *);
278278
// and if the validation is passed, it will set the ``tp_version`` as valid
279279
// tp_version_tag from the ``ty``.
280280
extern int _PyType_Validate(PyTypeObject *ty, _py_validate_type validate, unsigned int *tp_version);
281+
extern PyObject *_PyType_GetItemFromCache(PyTypeObject *type);
282+
extern PyObject *_PyType_GetItemFromCacheWithVersion(PyTypeObject *type, uint32_t *version);
283+
extern int _PyType_CacheGetItemForSpecialization(PyTypeObject *type, PyObject *descriptor);
281284

282285
#ifdef __cplusplus
283286
}

Include/internal/pycore_uop_metadata.h

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Lib/test/test_opcache.py

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -609,7 +609,7 @@ def assert_races_do_not_crash(
609609
for writer in writers:
610610
writer.join()
611611

612-
@requires_specialization
612+
@requires_specialization_ft
613613
def test_binary_subscr_getitem(self):
614614
def get_items():
615615
class C:
@@ -1069,7 +1069,7 @@ def write(items):
10691069
opname = "STORE_SUBSCR_LIST_INT"
10701070
self.assert_races_do_not_crash(opname, get_items, read, write)
10711071

1072-
@requires_specialization
1072+
@requires_specialization_ft
10731073
def test_unpack_sequence_list(self):
10741074
def get_items():
10751075
items = []
@@ -1520,6 +1520,20 @@ def binary_subscr_str_int():
15201520
self.assert_specialized(binary_subscr_str_int, "BINARY_SUBSCR_STR_INT")
15211521
self.assert_no_opcode(binary_subscr_str_int, "BINARY_SUBSCR")
15221522

1523+
def binary_subscr_getitems():
1524+
class C:
1525+
def __init__(self, val):
1526+
self.val = val
1527+
def __getitem__(self, item):
1528+
return self.val
1529+
items = [C(i) for i in range(100)]
1530+
for i in range(100):
1531+
self.assertEqual(items[i][i], i)
1532+
1533+
binary_subscr_getitems()
1534+
self.assert_specialized(binary_subscr_getitems, "BINARY_SUBSCR_GETITEM")
1535+
self.assert_no_opcode(binary_subscr_getitems, "BINARY_SUBSCR")
1536+
15231537

15241538
if __name__ == "__main__":
15251539
unittest.main()

Objects/typeobject.c

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5692,6 +5692,62 @@ _PyType_CacheInitForSpecialization(PyHeapTypeObject *type, PyObject *init,
56925692
return can_cache;
56935693
}
56945694

5695+
int
5696+
_PyType_CacheGetItemForSpecialization(PyTypeObject *type, PyObject *descriptor)
5697+
{
5698+
int can_cache = 0;
5699+
if (!type) {
5700+
return -1;
5701+
}
5702+
BEGIN_TYPE_LOCK();
5703+
// This pointer is invalidated by PyType_Modified (see the comment on
5704+
// struct _specialization_cache):
5705+
PyHeapTypeObject *ht = (PyHeapTypeObject *)type;
5706+
PyFunctionObject *func = (PyFunctionObject *)descriptor;
5707+
uint32_t version = _PyFunction_GetVersionForCurrentState(func);
5708+
if (!_PyFunction_IsVersionValid(version)) {
5709+
can_cache = -1;
5710+
goto end;
5711+
}
5712+
#ifdef Py_GIL_DISABLED
5713+
can_cache = _PyObject_HasDeferredRefcount(descriptor);
5714+
#else
5715+
can_cache = 0;
5716+
#endif
5717+
FT_ATOMIC_STORE_PTR_RELEASE(ht->_spec_cache.getitem, descriptor);
5718+
FT_ATOMIC_STORE_UINT32_RELAXED(ht->_spec_cache.getitem_version, version);
5719+
end:
5720+
END_TYPE_LOCK();
5721+
return can_cache;
5722+
}
5723+
5724+
PyObject *
5725+
_PyType_GetItemFromCache(PyTypeObject *type)
5726+
{
5727+
PyObject *res = NULL;
5728+
BEGIN_TYPE_LOCK();
5729+
PyHeapTypeObject *ht = (PyHeapTypeObject *)type;
5730+
res = ht->_spec_cache.getitem;
5731+
END_TYPE_LOCK();
5732+
return res;
5733+
}
5734+
5735+
PyObject *
5736+
_PyType_GetItemFromCacheWithVersion(PyTypeObject *type, uint32_t *version)
5737+
{
5738+
PyObject *res = NULL;
5739+
BEGIN_TYPE_LOCK();
5740+
PyHeapTypeObject *ht = (PyHeapTypeObject *)type;
5741+
res = ht->_spec_cache.getitem;
5742+
if (res == NULL) {
5743+
goto end;
5744+
}
5745+
*version = ht->_spec_cache.getitem_version;
5746+
end:
5747+
END_TYPE_LOCK();
5748+
return res;
5749+
}
5750+
56955751
static void
56965752
set_flags(PyTypeObject *self, unsigned long mask, unsigned long flags)
56975753
{

Python/bytecodes.c

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -867,11 +867,10 @@ dummy_func(
867867
op(_BINARY_SUBSCR_CHECK_FUNC, (container, unused -- container, unused)) {
868868
PyTypeObject *tp = Py_TYPE(PyStackRef_AsPyObjectBorrow(container));
869869
DEOPT_IF(!PyType_HasFeature(tp, Py_TPFLAGS_HEAPTYPE));
870-
PyHeapTypeObject *ht = (PyHeapTypeObject *)tp;
871-
PyObject *getitem = ht->_spec_cache.getitem;
870+
uint32_t cached_version;
871+
PyObject *getitem = _PyType_GetItemFromCacheWithVersion(tp, &cached_version);
872872
DEOPT_IF(getitem == NULL);
873873
assert(PyFunction_Check(getitem));
874-
uint32_t cached_version = ht->_spec_cache.getitem_version;
875874
DEOPT_IF(((PyFunctionObject *)getitem)->func_version != cached_version);
876875
PyCodeObject *code = (PyCodeObject *)PyFunction_GET_CODE(getitem);
877876
assert(code->co_argcount == 2);
@@ -881,8 +880,8 @@ dummy_func(
881880

882881
op(_BINARY_SUBSCR_INIT_CALL, (container, sub -- new_frame: _PyInterpreterFrame* )) {
883882
PyTypeObject *tp = Py_TYPE(PyStackRef_AsPyObjectBorrow(container));
884-
PyHeapTypeObject *ht = (PyHeapTypeObject *)tp;
885-
PyObject *getitem = ht->_spec_cache.getitem;
883+
PyObject *getitem = _PyType_GetItemFromCache(tp);
884+
DEOPT_IF(getitem == NULL);
886885
new_frame = _PyFrame_PushUnchecked(tstate, PyStackRef_FromPyObjectNew(getitem), 2, frame);
887886
new_frame->localsplus[0] = container;
888887
new_frame->localsplus[1] = sub;

Python/executor_cases.c.h

Lines changed: 11 additions & 5 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Python/generated_cases.c.h

Lines changed: 8 additions & 5 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Python/specialize.c

Lines changed: 7 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1085,6 +1085,7 @@ specialize_instance_load_attr(PyObject* owner, _Py_CODEUNIT* instr, PyObject* na
10851085
SPECIALIZATION_FAIL(LOAD_ATTR, SPEC_FAIL_ATTR_METHOD);
10861086
return -1;
10871087
}
1088+
/* Don't specialize if PEP 523 is active */
10881089
if (_PyInterpreterState_GET()->eval_frame) {
10891090
SPECIALIZATION_FAIL(LOAD_ATTR, SPEC_FAIL_OTHER);
10901091
return -1;
@@ -1154,6 +1155,7 @@ specialize_instance_load_attr(PyObject* owner, _Py_CODEUNIT* instr, PyObject* na
11541155
if (version == 0) {
11551156
return -1;
11561157
}
1158+
/* Don't specialize if PEP 523 is active */
11571159
if (_PyInterpreterState_GET()->eval_frame) {
11581160
SPECIALIZATION_FAIL(LOAD_ATTR, SPEC_FAIL_OTHER);
11591161
return -1;
@@ -1761,9 +1763,7 @@ _Py_Specialize_BinarySubscr(
17611763
specialized_op = BINARY_SUBSCR_DICT;
17621764
goto success;
17631765
}
1764-
#ifndef Py_GIL_DISABLED
1765-
PyTypeObject *cls = Py_TYPE(container);
1766-
PyObject *descriptor = _PyType_Lookup(cls, &_Py_ID(__getitem__));
1766+
PyObject *descriptor = _PyType_Lookup(container_type, &_Py_ID(__getitem__));
17671767
if (descriptor && Py_TYPE(descriptor) == &PyFunction_Type) {
17681768
if (!(container_type->tp_flags & Py_TPFLAGS_HEAPTYPE)) {
17691769
SPECIALIZATION_FAIL(BINARY_SUBSCR, SPEC_FAIL_SUBSCR_NOT_HEAP_TYPE);
@@ -1780,24 +1780,18 @@ _Py_Specialize_BinarySubscr(
17801780
SPECIALIZATION_FAIL(BINARY_SUBSCR, SPEC_FAIL_WRONG_NUMBER_ARGUMENTS);
17811781
goto fail;
17821782
}
1783-
uint32_t version = _PyFunction_GetVersionForCurrentState(func);
1784-
if (!_PyFunction_IsVersionValid(version)) {
1783+
if (_PyType_CacheGetItemForSpecialization(container_type, descriptor) < 0) {
17851784
SPECIALIZATION_FAIL(BINARY_SUBSCR, SPEC_FAIL_OUT_OF_VERSIONS);
17861785
goto fail;
17871786
}
1787+
/* Don't specialize if PEP 523 is active */
17881788
if (_PyInterpreterState_GET()->eval_frame) {
17891789
SPECIALIZATION_FAIL(BINARY_SUBSCR, SPEC_FAIL_OTHER);
17901790
goto fail;
17911791
}
1792-
PyHeapTypeObject *ht = (PyHeapTypeObject *)container_type;
1793-
// This pointer is invalidated by PyType_Modified (see the comment on
1794-
// struct _specialization_cache):
1795-
ht->_spec_cache.getitem = descriptor;
1796-
ht->_spec_cache.getitem_version = version;
17971792
specialized_op = BINARY_SUBSCR_GETITEM;
17981793
goto success;
17991794
}
1800-
#endif // Py_GIL_DISABLED
18011795
SPECIALIZATION_FAIL(BINARY_SUBSCR,
18021796
binary_subscr_fail_kind(container_type, sub));
18031797
fail:
@@ -2606,6 +2600,7 @@ _Py_Specialize_ForIter(_PyStackRef iter, _Py_CODEUNIT *instr, int oparg)
26062600
assert(instr[oparg + INLINE_CACHE_ENTRIES_FOR_ITER + 1].op.code == END_FOR ||
26072601
instr[oparg + INLINE_CACHE_ENTRIES_FOR_ITER + 1].op.code == INSTRUMENTED_END_FOR
26082602
);
2603+
/* Don't specialize if PEP 523 is active */
26092604
if (_PyInterpreterState_GET()->eval_frame) {
26102605
SPECIALIZATION_FAIL(FOR_ITER, SPEC_FAIL_OTHER);
26112606
goto failure;
@@ -2634,6 +2629,7 @@ _Py_Specialize_Send(_PyStackRef receiver_st, _Py_CODEUNIT *instr)
26342629
assert(_PyOpcode_Caches[SEND] == INLINE_CACHE_ENTRIES_SEND);
26352630
PyTypeObject *tp = Py_TYPE(receiver);
26362631
if (tp == &PyGen_Type || tp == &PyCoro_Type) {
2632+
/* Don't specialize if PEP 523 is active */
26372633
if (_PyInterpreterState_GET()->eval_frame) {
26382634
SPECIALIZATION_FAIL(SEND, SPEC_FAIL_OTHER);
26392635
goto failure;

0 commit comments

Comments
 (0)