mirror of
https://github.com/ruby/ruby.git
synced 2026-08-05 11:19:10 +08:00
ZJIT: Use counted side exits for send fallbacks while tracing
Instead of blocking compilation entirely when tracing is active, emit counted side exits in HIR before send fallback instructions that use VM_EXEC (which sets FLAG_FINISH on the callee frame, breaking throw TAG_RETURN semantics). This gives us runtime stats on how often tracing causes side exits while still allowing compilation. The tracing check is done at HIR construction time so that Send fallback instructions are never emitted into the IR when tracing is active, replaced by SideExit instructions instead. Affected YARV instructions: opt_neq, opt_send_without_block (and all opt_* variants), send, sendforward, invokesuper, invokesuperforward, invokeblock.
This commit is contained in:
parent
e7fa7293fd
commit
af459daea4
Notes:
git
2026-03-20 01:06:38 +00:00
11
vm.c
11
vm.c
@ -533,23 +533,12 @@ yjit_compile(rb_execution_context_t *ec)
|
||||
#endif
|
||||
|
||||
#if USE_ZJIT
|
||||
bool rb_zjit_iseq_tracing_currently_enabled(void);
|
||||
|
||||
static inline rb_jit_func_t
|
||||
zjit_compile(rb_execution_context_t *ec)
|
||||
{
|
||||
const rb_iseq_t *iseq = ec->cfp->iseq;
|
||||
struct rb_iseq_constant_body *body = ISEQ_BODY(iseq);
|
||||
|
||||
// Don't compile while tracing is active. ZJIT's send fallback uses
|
||||
// rb_vm_opt_send_without_block which calls VM_EXEC, setting FLAG_FINISH
|
||||
// on the callee frame. This changes exception handling semantics for
|
||||
// throw TAG_RETURN (e.g. return from rescue), causing TracePoint to
|
||||
// report nil instead of the actual return value. [Bug #21389]
|
||||
if (rb_zjit_iseq_tracing_currently_enabled()) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (body->jit_entry == NULL) {
|
||||
body->jit_entry_calls++;
|
||||
|
||||
|
||||
@ -292,6 +292,7 @@ fn main() {
|
||||
.allowlist_function("rb_set_cfp_(pc|sp)")
|
||||
.allowlist_function("rb_c_method_tracing_currently_enabled")
|
||||
.allowlist_function("rb_zjit_method_tracing_currently_enabled")
|
||||
.allowlist_function("rb_zjit_iseq_tracing_currently_enabled")
|
||||
.allowlist_function("rb_full_cfunc_return")
|
||||
.allowlist_function("rb_assert_(iseq|cme)_handle")
|
||||
.allowlist_function("rb_IMEMO_TYPE_P")
|
||||
|
||||
1
zjit/src/cruby_bindings.inc.rs
generated
1
zjit/src/cruby_bindings.inc.rs
generated
@ -2151,6 +2151,7 @@ unsafe extern "C" {
|
||||
pub fn rb_zjit_singleton_class_p(klass: VALUE) -> bool;
|
||||
pub fn rb_zjit_defined_ivar(obj: VALUE, id: ID, pushval: VALUE) -> VALUE;
|
||||
pub fn rb_zjit_method_tracing_currently_enabled() -> bool;
|
||||
pub fn rb_zjit_iseq_tracing_currently_enabled() -> bool;
|
||||
pub fn rb_zjit_insn_leaf(insn: ::std::os::raw::c_int, opes: *const VALUE) -> bool;
|
||||
pub fn rb_zjit_local_id(iseq: *const rb_iseq_t, idx: ::std::os::raw::c_uint) -> ID;
|
||||
pub fn rb_zjit_cme_is_cfunc(
|
||||
|
||||
@ -532,6 +532,7 @@ pub enum SideExitReason {
|
||||
SplatKwPolymorphic,
|
||||
SplatKwNotProfiled,
|
||||
DirectiveInduced,
|
||||
SendWhileTracing,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
@ -7509,6 +7510,11 @@ pub fn iseq_to_hir(iseq: *const rb_iseq_t) -> Result<Function, ParseError> {
|
||||
}
|
||||
let argc = unsafe { vm_ci_argc((*cd).ci) };
|
||||
|
||||
// Side-exit send fallbacks while tracing to avoid FLAG_FINISH breaking throw TAG_RETURN semantics
|
||||
if unsafe { rb_zjit_iseq_tracing_currently_enabled() } {
|
||||
fun.push_insn(block, Insn::SideExit { state: exit_id, reason: SideExitReason::SendWhileTracing });
|
||||
break;
|
||||
}
|
||||
let args = state.stack_pop_n(argc as usize)?;
|
||||
let recv = state.stack_pop()?;
|
||||
let send = fun.push_insn(block, Insn::Send { recv, cd, blockiseq: None, args, state: exit_id, reason: Uncategorized(opcode) });
|
||||
@ -7638,6 +7644,12 @@ pub fn iseq_to_hir(iseq: *const rb_iseq_t) -> Result<Function, ParseError> {
|
||||
}
|
||||
}
|
||||
|
||||
// Side-exit send fallbacks while tracing to avoid FLAG_FINISH breaking throw TAG_RETURN semantics
|
||||
if unsafe { rb_zjit_iseq_tracing_currently_enabled() } {
|
||||
fun.push_insn(block, Insn::SideExit { state: exit_id, reason: SideExitReason::SendWhileTracing });
|
||||
break;
|
||||
}
|
||||
|
||||
{
|
||||
fn new_branch_block(
|
||||
fun: &mut Function,
|
||||
@ -7724,6 +7736,11 @@ pub fn iseq_to_hir(iseq: *const rb_iseq_t) -> Result<Function, ParseError> {
|
||||
fun.push_insn(block, Insn::SideExit { state: exit_id, reason: SideExitReason::UnhandledCallType(call_type) });
|
||||
break; // End the block
|
||||
}
|
||||
// Side-exit send fallbacks while tracing to avoid FLAG_FINISH breaking throw TAG_RETURN semantics
|
||||
if unsafe { rb_zjit_iseq_tracing_currently_enabled() } {
|
||||
fun.push_insn(block, Insn::SideExit { state: exit_id, reason: SideExitReason::SendWhileTracing });
|
||||
break;
|
||||
}
|
||||
let argc = unsafe { vm_ci_argc((*cd).ci) };
|
||||
let block_arg = (flags & VM_CALL_ARGS_BLOCKARG) != 0;
|
||||
|
||||
@ -7758,6 +7775,11 @@ pub fn iseq_to_hir(iseq: *const rb_iseq_t) -> Result<Function, ParseError> {
|
||||
fun.push_insn(block, Insn::SideExit { state: exit_id, reason: SideExitReason::UnhandledCallType(call_type) });
|
||||
break; // End the block
|
||||
}
|
||||
// Side-exit send fallbacks while tracing to avoid FLAG_FINISH breaking throw TAG_RETURN semantics
|
||||
if unsafe { rb_zjit_iseq_tracing_currently_enabled() } {
|
||||
fun.push_insn(block, Insn::SideExit { state: exit_id, reason: SideExitReason::SendWhileTracing });
|
||||
break;
|
||||
}
|
||||
let argc = unsafe { vm_ci_argc((*cd).ci) };
|
||||
|
||||
let args = state.stack_pop_n(argc as usize + usize::from(forwarding))?;
|
||||
@ -7787,6 +7809,11 @@ pub fn iseq_to_hir(iseq: *const rb_iseq_t) -> Result<Function, ParseError> {
|
||||
fun.push_insn(block, Insn::SideExit { state: exit_id, reason: SideExitReason::UnhandledCallType(call_type) });
|
||||
break; // End the block
|
||||
}
|
||||
// Side-exit send fallbacks while tracing to avoid FLAG_FINISH breaking throw TAG_RETURN semantics
|
||||
if unsafe { rb_zjit_iseq_tracing_currently_enabled() } {
|
||||
fun.push_insn(block, Insn::SideExit { state: exit_id, reason: SideExitReason::SendWhileTracing });
|
||||
break;
|
||||
}
|
||||
let argc = unsafe { vm_ci_argc((*cd).ci) };
|
||||
let block_arg = (flags & VM_CALL_ARGS_BLOCKARG) != 0;
|
||||
let args = state.stack_pop_n(argc as usize + usize::from(block_arg))?;
|
||||
@ -7821,6 +7848,11 @@ pub fn iseq_to_hir(iseq: *const rb_iseq_t) -> Result<Function, ParseError> {
|
||||
fun.push_insn(block, Insn::SideExit { state: exit_id, reason: SideExitReason::UnhandledCallType(call_type) });
|
||||
break; // End the block
|
||||
}
|
||||
// Side-exit send fallbacks while tracing to avoid FLAG_FINISH breaking throw TAG_RETURN semantics
|
||||
if unsafe { rb_zjit_iseq_tracing_currently_enabled() } {
|
||||
fun.push_insn(block, Insn::SideExit { state: exit_id, reason: SideExitReason::SendWhileTracing });
|
||||
break;
|
||||
}
|
||||
let argc = unsafe { vm_ci_argc((*cd).ci) };
|
||||
let args = state.stack_pop_n(argc as usize + usize::from(forwarding))?;
|
||||
let recv = state.stack_pop()?;
|
||||
@ -7851,6 +7883,11 @@ pub fn iseq_to_hir(iseq: *const rb_iseq_t) -> Result<Function, ParseError> {
|
||||
fun.push_insn(block, Insn::SideExit { state: exit_id, reason: SideExitReason::UnhandledCallType(call_type) });
|
||||
break; // End the block
|
||||
}
|
||||
// Side-exit send fallbacks while tracing to avoid FLAG_FINISH breaking throw TAG_RETURN semantics
|
||||
if unsafe { rb_zjit_iseq_tracing_currently_enabled() } {
|
||||
fun.push_insn(block, Insn::SideExit { state: exit_id, reason: SideExitReason::SendWhileTracing });
|
||||
break;
|
||||
}
|
||||
let argc = unsafe { vm_ci_argc((*cd).ci) };
|
||||
let block_arg = (flags & VM_CALL_ARGS_BLOCKARG) != 0;
|
||||
let args = state.stack_pop_n(argc as usize + usize::from(block_arg))?;
|
||||
|
||||
@ -232,6 +232,7 @@ make_counters! {
|
||||
exit_splatkw_polymorphic,
|
||||
exit_splatkw_not_profiled,
|
||||
exit_directive_induced,
|
||||
exit_send_while_tracing,
|
||||
}
|
||||
|
||||
// Send fallback counters that are summed as dynamic_send_count
|
||||
@ -616,6 +617,7 @@ pub fn side_exit_counter(reason: crate::hir::SideExitReason) -> Counter {
|
||||
=> exit_patchpoint_no_singleton_class,
|
||||
PatchPoint(Invariant::RootBoxOnly)
|
||||
=> exit_patchpoint_root_box_only,
|
||||
SendWhileTracing => exit_send_while_tracing,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user