You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
While investigating, make JIT thread-safe for FT build.
IMO, we should separate this task into making the tier 2 interpreter thread-safe (with optimzation disabled) and the optimizer thread-safe.
But we notice that the current no-opt JIT at the default build does not pass the whole unittest.
Let's make no-opt JIT the default build pass, whole unit tests.
I plan to investigate the issue, please let me know if I should take care of something since I am not an expert on this topic.
@Fidget-Spinner I didn't take a look at uop_trace deeply inside(which codes are generated, something like that), but simply increased UOP_MAX_TRACE_LENGTH to 1600, specifically failed test code, but it will not be the answer.
Possibly unoptimized traced code emits too much long trace code?
The problem is that prepare_for_execution inserts more exit stubs (_EXIT_TRACE, _DEOPT, _ERROR_N) that overrun the buffer.
Basically the flow is like this:
translate_bytecode_to_trace (uops without exit stubs)
optimize (shortens the uops)
prepare_for_execution (inserts the exit stubs)
The problem is that translate_bytecode_to_trace seems to not be properly reserving enough space in the uop buffer for the number of possible exit stubs needed.
So there's a big problem right now that isn't easily solved: the calculation for side exit stubs to reserve when there's no optimizer and with optimizer is different. Let me explain:
Suppose you have the following sequence of uops, all decomposed from the same bytecode:
_CHECK_VALIDITY
_UOP_1
_UOP_2
The side exit calculation with optimizer on is pretty smart, just insert need two exit stubs, one for _CHECK_VALIDITY if needed, and one for the two _UOPS. The second exit stub can reused for the last two uops together.
Now suppose we have no optimizer, that means we have a lot of redundant _CHECK_VALIDITY in between:
_CHECK_VALIDITY
_UOP_1
_CHECK_VALIDITY
_UOP_2
Note that now, _UOP_1 and _UOP_2 cannot share the same exit stub using our naiive exit stub sharing algo in the prepare_execution function, they must have separate ones.
So the problem is:
When optimizer is on: side exit is per opcode as they will be shared across the uops
When the optimizer is off: side exit is per uop as they cannot be shared across the uops.
There is no easy way to solve it by refining our calculation. I think we should simply reserve more space or project a smaller trace by overestimating.
Activity
corona10 commentedon Aug 15, 2025
corona10 commentedon Aug 16, 2025
corona10 commentedon Aug 16, 2025
@Fidget-Spinner I didn't take a look at uop_trace deeply inside(which codes are generated, something like that), but simply increased UOP_MAX_TRACE_LENGTH to 1600, specifically failed test code, but it will not be the answer.
Possibly unoptimized traced code emits too much long trace code?
Fidget-Spinner commentedon Aug 16, 2025
The problem is that
prepare_for_executioninserts more exit stubs (_EXIT_TRACE, _DEOPT, _ERROR_N) that overrun the buffer.Basically the flow is like this:
The problem is that
translate_bytecode_to_traceseems to not be properly reserving enough space in the uop buffer for the number of possible exit stubs needed.pythongh-137838: Fix JIT trace buffer overrun by pre-reserving exit s…
Revert "pythongh-137838: Fix JIT trace buffer overrun by pre-reservin…
pythongh-137838: Fix JIT trace buffer overrun by increasing possible …
corona10 commentedon Aug 27, 2025
Summary discussion w/ @Fidget-Spinner and CHIPS
Fix calculation bug that @Fidget-Spinner found(?)Fidget-Spinner commentedon Aug 28, 2025
So there's a big problem right now that isn't easily solved: the calculation for side exit stubs to reserve when there's no optimizer and with optimizer is different. Let me explain:
Suppose you have the following sequence of uops, all decomposed from the same bytecode:
The side exit calculation with optimizer on is pretty smart, just insert need two exit stubs, one for _CHECK_VALIDITY if needed, and one for the two _UOPS. The second exit stub can reused for the last two uops together.
Now suppose we have no optimizer, that means we have a lot of redundant _CHECK_VALIDITY in between:
Note that now, _UOP_1 and _UOP_2 cannot share the same exit stub using our naiive exit stub sharing algo in the
prepare_executionfunction, they must have separate ones.So the problem is:
There is no easy way to solve it by refining our calculation. I think we should simply reserve more space or project a smaller trace by overestimating.
corona10 commentedon Aug 28, 2025
That would be my one suggstion, something like this: 917c6f0 at #138177
gh-137838: Fix JIT trace buffer overrun by increasing possible exit s…
pythongh-137838: Fix JIT trace buffer overrun by increasing possible …
corona10 commentedon Sep 14, 2025
Plan to work on during the core sprint cc @Fidget-Spinner
pythongh-137838: Move _PyUOpInstruction buffer[UOP_MAX_TRACE_LENGTH] …
gh-137838: Move _PyUOpInstruction buffer to PyInterpreterState (gh-13…
pythongh-137838: Add CI for no-opt JIT
pythongh-137838: Add CI for no-opt JIT
gh-137838: Add CI for no-opt JIT (#139081)
corona10 commentedon Sep 18, 2025
This was completed during the sprint! Thanks for the support, @Fidget-Spinner and @brandtbucher!