Follow-up to ruby#17457 (comment) and the earlier discussion at ruby#17326 (comment).
PR ruby#17457 fixes the smaller deduplication issue by sharing the per-instruction profiling logic used by zjit_ YARV instructions. After that PR, recompile exits can recover the current instruction from the materialized CFP and profile that instruction through profile.rs instead of carrying kind-specific profiling payloads across the C ABI.
The remaining follow-up is to change the recompile-exit lifecycle so the interpreter actually executes zjit_ YARV profiling instructions again, rather than having the ZJIT side-exit hook directly call into the profiling implementation for only the current instruction.
Problem
Today, a recompile side exit profiles only the instruction that caused the exit. That is enough to fix the immediate side exit, but it means an ISEQ with multiple no-profile callsites may need multiple recompile versions: one exit/version to discover each missing profile.
For example, if an ISEQ has N no-profile callsites, the current behavior can require up to N recompile exits because each exit only profiles the callsite that happened to exit. If the side exit instead disabled the current compiled version and re-enabled interpreter profiling for the ISEQ, the next interpreter pass could collect profiles for the exiting instruction and for other zjit_-profiled instructions in the same ISEQ before compiling the next version.
Desired behavior
When a side exit with recompile metadata is hit:
- Disable/invalidate the current compiled version, preserving the existing distinction between the frame/current ISEQ being profiled and the compiled ISEQ whose version must be invalidated for inlined exits.
- Re-enable
zjit_ YARV instructions so the interpreter profiles the ISEQ through the normal rb_zjit_profile_insn path.
- Restart the relevant
profiles_remaining counters so this is a real --zjit-num-profiles profiling window, not just a one-off sample for entries that had already finished profiling.
- Let the normal profile-threshold/call-threshold path compile the next version after the interpreter has had a chance to collect whole-ISEQ profile data.
- Keep the max-version/final-version behavior intact: if the ISEQ cannot be recompiled, the side exit should not keep re-enabling profiling or churning counters.
Follow-up to ruby#17457 (comment) and the earlier discussion at ruby#17326 (comment).
PR ruby#17457 fixes the smaller deduplication issue by sharing the per-instruction profiling logic used by
zjit_YARV instructions. After that PR, recompile exits can recover the current instruction from the materialized CFP and profile that instruction throughprofile.rsinstead of carrying kind-specific profiling payloads across the C ABI.The remaining follow-up is to change the recompile-exit lifecycle so the interpreter actually executes
zjit_YARV profiling instructions again, rather than having the ZJIT side-exit hook directly call into the profiling implementation for only the current instruction.Problem
Today, a recompile side exit profiles only the instruction that caused the exit. That is enough to fix the immediate side exit, but it means an ISEQ with multiple no-profile callsites may need multiple recompile versions: one exit/version to discover each missing profile.
For example, if an ISEQ has N no-profile callsites, the current behavior can require up to N recompile exits because each exit only profiles the callsite that happened to exit. If the side exit instead disabled the current compiled version and re-enabled interpreter profiling for the ISEQ, the next interpreter pass could collect profiles for the exiting instruction and for other
zjit_-profiled instructions in the same ISEQ before compiling the next version.Desired behavior
When a side exit with
recompilemetadata is hit:zjit_YARV instructions so the interpreter profiles the ISEQ through the normalrb_zjit_profile_insnpath.profiles_remainingcounters so this is a real--zjit-num-profilesprofiling window, not just a one-off sample for entries that had already finished profiling.