-
-
Notifications
You must be signed in to change notification settings - Fork 33.1k
gh-115999: Add free-threaded specialization for FOR_ITER #128798
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
Changes from 5 commits
54c551e
1433cd3
a662ecf
2fef94b
0870ce7
cf5fcd0
bb495b0
940b7c9
a800d75
1781cad
358199a
5c16a5e
07d3033
4326376
375399d
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -2929,15 +2929,15 @@ dummy_func( | |
}; | ||
|
||
specializing op(_SPECIALIZE_FOR_ITER, (counter/1, iter -- iter)) { | ||
#if ENABLE_SPECIALIZATION | ||
#if ENABLE_SPECIALIZATION_FT | ||
if (ADAPTIVE_COUNTER_TRIGGERS(counter)) { | ||
next_instr = this_instr; | ||
_Py_Specialize_ForIter(iter, next_instr, oparg); | ||
DISPATCH_SAME_OPARG(); | ||
} | ||
OPCODE_DEFERRED_INC(FOR_ITER); | ||
ADVANCE_ADAPTIVE_COUNTER(this_instr[1].counter); | ||
#endif /* ENABLE_SPECIALIZATION */ | ||
#endif /* ENABLE_SPECIALIZATION_FT */ | ||
} | ||
|
||
replaced op(_FOR_ITER, (iter -- iter, next)) { | ||
|
@@ -3015,10 +3015,20 @@ dummy_func( | |
|
||
|
||
op(_ITER_CHECK_LIST, (iter -- iter)) { | ||
EXIT_IF(Py_TYPE(PyStackRef_AsPyObjectBorrow(iter)) != &PyListIter_Type); | ||
PyObject *iter_o = PyStackRef_AsPyObjectBorrow(iter); | ||
EXIT_IF(Py_TYPE(iter_o) != &PyListIter_Type); | ||
#ifdef Py_GIL_DISABLED | ||
_PyListIterObject *it = (_PyListIterObject *)iter_o; | ||
EXIT_IF(it->it_seq == NULL || | ||
Yhg1s marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
!_Py_IsOwnedByCurrentThread((PyObject *)it->it_seq) || | ||
!_PyObject_GC_IS_SHARED(it->it_seq)); | ||
Yhg1s marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
#endif | ||
} | ||
|
||
replaced op(_ITER_JUMP_LIST, (iter -- iter)) { | ||
// For free-threaded Python, the loop exit can happen at any point during item | ||
// retrieval, so separate ops don't make much sense. | ||
|
||
#ifndef Py_GIL_DISABLED | ||
Yhg1s marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
PyObject *iter_o = PyStackRef_AsPyObjectBorrow(iter); | ||
_PyListIterObject *it = (_PyListIterObject *)iter_o; | ||
assert(Py_TYPE(iter_o) == &PyListIter_Type); | ||
|
@@ -3036,10 +3046,12 @@ dummy_func( | |
JUMPBY(oparg + 1); | ||
DISPATCH(); | ||
} | ||
#endif | ||
} | ||
|
||
// Only used by Tier 2 | ||
op(_GUARD_NOT_EXHAUSTED_LIST, (iter -- iter)) { | ||
#ifndef Py_GIL_DISABLED | ||
PyObject *iter_o = PyStackRef_AsPyObjectBorrow(iter); | ||
_PyListIterObject *it = (_PyListIterObject *)iter_o; | ||
assert(Py_TYPE(iter_o) == &PyListIter_Type); | ||
|
@@ -3049,6 +3061,7 @@ dummy_func( | |
it->it_index = -1; | ||
EXIT_IF(1); | ||
} | ||
#endif | ||
} | ||
|
||
op(_ITER_NEXT_LIST, (iter -- iter, next)) { | ||
|
@@ -3057,8 +3070,30 @@ dummy_func( | |
assert(Py_TYPE(iter_o) == &PyListIter_Type); | ||
PyListObject *seq = it->it_seq; | ||
assert(seq); | ||
#ifdef Py_GIL_DISABLED | ||
assert(_Py_IsOwnedByCurrentThread((PyObject *)seq) || | ||
_PyObject_GC_IS_SHARED(seq)); | ||
STAT_INC(FOR_ITER, hit); | ||
Py_ssize_t idx = _Py_atomic_load_ssize_relaxed(&it->it_index); | ||
PyObject *item; | ||
int result = _PyList_GetItemRefNoLock(it->it_seq, idx, &item); | ||
// A negative result means we lost a race with another thread | ||
// and we need to take the slow path. | ||
EXIT_IF(result < 0); | ||
Yhg1s marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
if (result == 0) { | ||
_Py_atomic_store_ssize_relaxed(&it->it_index, -1); | ||
PyStackRef_CLOSE(iter); | ||
STACK_SHRINK(1); | ||
/* Jump forward oparg, then skip following END_FOR and POP_TOP instructions */ | ||
JUMPBY(oparg + 2); | ||
DISPATCH(); | ||
} | ||
_Py_atomic_store_ssize_relaxed(&it->it_index, idx + 1); | ||
next = PyStackRef_FromPyObjectSteal(item); | ||
#else | ||
assert(it->it_index < PyList_GET_SIZE(seq)); | ||
next = PyStackRef_FromPyObjectNew(PyList_GET_ITEM(seq, it->it_index++)); | ||
#endif | ||
} | ||
|
||
macro(FOR_ITER_LIST) = | ||
|
@@ -3073,8 +3108,11 @@ dummy_func( | |
|
||
replaced op(_ITER_JUMP_TUPLE, (iter -- iter)) { | ||
PyObject *iter_o = PyStackRef_AsPyObjectBorrow(iter); | ||
_PyTupleIterObject *it = (_PyTupleIterObject *)iter_o; | ||
assert(Py_TYPE(iter_o) == &PyTupleIter_Type); | ||
// For free-threaded Python, the loop exit can happen at any point during item | ||
// retrieval, so separate ops don't make much sense. | ||
|
||
#ifndef Py_GIL_DISABLED | ||
_PyTupleIterObject *it = (_PyTupleIterObject *)iter_o; | ||
STAT_INC(FOR_ITER, hit); | ||
PyTupleObject *seq = it->it_seq; | ||
if (seq == NULL || it->it_index >= PyTuple_GET_SIZE(seq)) { | ||
|
@@ -3086,26 +3124,44 @@ dummy_func( | |
JUMPBY(oparg + 1); | ||
DISPATCH(); | ||
} | ||
#endif | ||
} | ||
|
||
// Only used by Tier 2 | ||
op(_GUARD_NOT_EXHAUSTED_TUPLE, (iter -- iter)) { | ||
PyObject *iter_o = PyStackRef_AsPyObjectBorrow(iter); | ||
_PyTupleIterObject *it = (_PyTupleIterObject *)iter_o; | ||
assert(Py_TYPE(iter_o) == &PyTupleIter_Type); | ||
#ifndef Py_GIL_DISABLED | ||
PyTupleObject *seq = it->it_seq; | ||
EXIT_IF(seq == NULL); | ||
EXIT_IF(it->it_index >= PyTuple_GET_SIZE(seq)); | ||
#endif | ||
} | ||
|
||
op(_ITER_NEXT_TUPLE, (iter -- iter, next)) { | ||
PyObject *iter_o = PyStackRef_AsPyObjectBorrow(iter); | ||
_PyTupleIterObject *it = (_PyTupleIterObject *)iter_o; | ||
assert(Py_TYPE(iter_o) == &PyTupleIter_Type); | ||
PyTupleObject *seq = it->it_seq; | ||
#ifdef Py_GIL_DISABLED | ||
STAT_INC(FOR_ITER, hit); | ||
Py_ssize_t idx = _Py_atomic_load_ssize_relaxed(&it->it_index); | ||
if (seq == NULL || (size_t)idx >= (size_t)PyTuple_GET_SIZE(seq)) { | ||
_Py_atomic_store_ssize_relaxed(&it->it_index, -1); | ||
PyStackRef_CLOSE(iter); | ||
STACK_SHRINK(1); | ||
/* Jump forward oparg, then skip following END_FOR and POP_TOP instructions */ | ||
JUMPBY(oparg + 2); | ||
DISPATCH(); | ||
} | ||
_Py_atomic_store_ssize_relaxed(&it->it_index, idx + 1); | ||
next = PyStackRef_FromPyObjectNew(PyTuple_GET_ITEM(seq, idx)); | ||
#else | ||
assert(seq); | ||
assert(it->it_index < PyTuple_GET_SIZE(seq)); | ||
next = PyStackRef_FromPyObjectNew(PyTuple_GET_ITEM(seq, it->it_index++)); | ||
#endif | ||
} | ||
|
||
macro(FOR_ITER_TUPLE) = | ||
|
@@ -3123,7 +3179,7 @@ dummy_func( | |
_PyRangeIterObject *r = (_PyRangeIterObject *)PyStackRef_AsPyObjectBorrow(iter); | ||
assert(Py_TYPE(r) == &PyRangeIter_Type); | ||
STAT_INC(FOR_ITER, hit); | ||
if (r->len <= 0) { | ||
if (FT_ATOMIC_LOAD_LONG_RELAXED(r->len) <= 0) { | ||
// Jump over END_FOR instruction. | ||
JUMPBY(oparg + 1); | ||
DISPATCH(); | ||
|
@@ -3134,16 +3190,19 @@ dummy_func( | |
op(_GUARD_NOT_EXHAUSTED_RANGE, (iter -- iter)) { | ||
_PyRangeIterObject *r = (_PyRangeIterObject *)PyStackRef_AsPyObjectBorrow(iter); | ||
assert(Py_TYPE(r) == &PyRangeIter_Type); | ||
EXIT_IF(r->len <= 0); | ||
EXIT_IF(FT_ATOMIC_LOAD_LONG_RELAXED(r->len) <= 0); | ||
} | ||
|
||
op(_ITER_NEXT_RANGE, (iter -- iter, next)) { | ||
_PyRangeIterObject *r = (_PyRangeIterObject *)PyStackRef_AsPyObjectBorrow(iter); | ||
assert(Py_TYPE(r) == &PyRangeIter_Type); | ||
#ifndef Py_GIL_DISABLED | ||
assert(r->len > 0); | ||
long value = r->start; | ||
r->start = value + r->step; | ||
r->len--; | ||
#endif | ||
long value = FT_ATOMIC_LOAD_LONG_RELAXED(r->start); | ||
FT_ATOMIC_STORE_LONG_RELAXED(r->start, value + r->step); | ||
FT_ATOMIC_STORE_LONG_RELAXED(r->len, | ||
FT_ATOMIC_LOAD_LONG_RELAXED(r->len) - 1); | ||
PyObject *res = PyLong_FromLong(value); | ||
ERROR_IF(res == NULL, error); | ||
next = PyStackRef_FromPyObjectSteal(res); | ||
|
Uh oh!
There was an error while loading. Please reload this page.