will_tailcall_bar() runs in the interpreter and sets up a tailcall. Right before JIT_EXEC() in the send instruction, the stack will look like:
bar [FINISH] caller_that_used_fallback
Previously, JIT_EXEC() ran bar() in JIT code, which caused the FINISH flag to return to the interpreter instead of to the JIT code running caller_that_used_fallback(), causing code to run twice and probably crash. Recent flaky failures on CI about "each stub expects a particular iseq" are probably due to leaving methods twice in test_optimizations.rb.
Only run JIT code from the interpreter if a new frame is pushed.
YJIT: Fix tailcall and JIT entry eating up FINISH frames (#9729)
Suppose YJIT runs a rb_vm_opt_send_without_block()
fallback and the control frame stack looks like:
will_tailcall_bar() runs in the interpreter and sets up a tailcall.
Right before JIT_EXEC() in the
sendinstruction, the stack will look like:Previously, JIT_EXEC() ran bar() in JIT code, which caused the
FINISHflag to return to the interpreter instead of to the JIT code running
caller_that_used_fallback(), causing code to run twice and probably
crash. Recent flaky failures on CI about "each stub expects a particular
iseq" are probably due to leaving methods twice in
test_optimizations.rb.Only run JIT code from the interpreter if a new frame is pushed.