gh-143732: add specialization for FOR_ITER#148745
gh-143732: add specialization for FOR_ITER#148745NekoAsakura wants to merge 12 commits intopython:mainfrom
FOR_ITER#148745Conversation
cocolato
left a comment
There was a problem hiding this comment.
LGTM, thanks for doing this!
| } | ||
|
|
||
| macro(FOR_ITER) = _SPECIALIZE_FOR_ITER + _FOR_ITER; | ||
| macro(FOR_ITER) = _SPECIALIZE_FOR_ITER + _RECORD_NOS_GEN_FUNC + _RECORD_NOS_TYPE + _FOR_ITER; |
There was a problem hiding this comment.
cpython/Python/optimizer_symbols.c
Lines 725 to 726 in ad7d361
We don't need
_RECORD_NOS_GEN_FUNC here, because the GEN_FUNC_TAG recorded here does not contribute to the current optimization.
There was a problem hiding this comment.
Every FOR_ITER specialisation's record list must be a prefix of FOR_ITER's.
_RECORD_NOS_GEN_FUNC writes a gen func or NULL to slot 0, matching what FOR_ITER_GEN reads from it.
https://github.com/python/cpython/actions/runs/24623062259/job/71997168333
| PyType_Watch(TYPE_WATCHER_ID, (PyObject *)probable); | ||
| _Py_BloomFilter_Add(dependencies, probable); | ||
| sym_set_type(iter, probable); | ||
| int32_t orig_target = (this_instr - 1)->target; |
There was a problem hiding this comment.
We should add an assert to make sure the last uop is _RECORD_NOS_TYPE
markshannon
left a comment
There was a problem hiding this comment.
I've a few suggestions inline.
| } | ||
|
|
||
| tier2 op(_ITER_NEXT_INLINE, (iternext_fn/4, iter, null_or_index -- iter, null_or_index, next)) { | ||
| volatile iternextfunc iternext_v = (iternextfunc)iternext_fn; |
There was a problem hiding this comment.
Why volatile? It shouldn't be necessary.
Also function pointers may not be the same size as normal pointers.
Can you add assert(sizeof(iternextfunc) == sizeof(uintptr_t)); to be on the safe side.
There was a problem hiding this comment.
Because mmap pages usually sit too far from tp_iternext for the offset to reach, we have to use volatile to force compiler to emit callq *%rax (target read from a register) instead of callq <rel32> (target baked in as a fixed offset). Otherwise the call jumps to the wrong address and segfaults.
There was a problem hiding this comment.
Does this fail just for x86 or for AArch64 as well?
This is a bug in the JIT and we fix it, rather than add workarounds.
I think we should be adding a trampoline here, but it seems that we are not.
If you're looking at the stencils, can you see how it is being patched (what patch function is being called)?
There was a problem hiding this comment.
Both. (aarch64 was tested under qemu-user)
Trampoline path doesn't appear to be designed for _JIT_* symbols:
cpython/Tools/jit/_stencils.py
Lines 434 to 446 in 7c9ad27
cpython/Tools/jit/_stencils.py
Lines 288 to 292 in 7c9ad27
cpython/Tools/jit/_stencils.py
Lines 305 to 309 in 7c9ad27
Even if we extended it to those, we'd need a stub per instance. I don't think it gains us anything.
There was a problem hiding this comment.
We only need a trampoline if the jitted code is too far from the executable. See #148822
|
A Python core developer has requested some changes be made to your pull request before we can consider merging it. If you could please address their requests along with any other requests in other reviews from core developers that would be appreciated. Once you have made the requested changes, please leave a comment on this pull request containing the phrase And if you don't make the requested changes, you will be poked with soft cushions! |
Documentation build overview
97 files changed ·
|
|
|
||
| macro(FOR_ITER_VIRTUAL) = | ||
| unused/1 + // Skip over the counter | ||
| _RECORD_NOS + |
There was a problem hiding this comment.
Yeah, this is by design from #148730. When picking the family-wide recorder, the cases generator only looks at members, not the head:
cpython/Tools/cases_generator/record_function_generator.py
Lines 88 to 89 in 7c9ad27
So without
_RECORD_NOS on a member somewhere, _RECORD_NOS_TYPE on FOR_ITER and _RECORD_NOS_GEN_FUNC on FOR_ITER_GEN will clash. I can add a comment about this.
| op(_FOR_ITER_TIER_TWO, (iter, null_or_index -- iter, null_or_index, next)) { | ||
| PyTypeObject *type = sym_get_type(iter); | ||
| if (type != NULL && type != &PyGen_Type && type->tp_iternext != NULL) { | ||
| ADD_OP(_ITER_NEXT_INLINE, 0, (uintptr_t)type->tp_iternext); |
There was a problem hiding this comment.
We need the watcher here as well.
What we do elsewhere is something like this:
bool definite = true;
PyTypeObject *type = sym_get_type(iter);
if (type == NULL) {
type = sym_get_probable_type(iter);
definite = false;
}
if (type != NULL) {
// Add if not definite, otherwise NOP
// Add watcher(s)
}
Uh oh!
There was an error while loading. Please reload this page.