ZJIT: Add counters for throw and exception_handler (#17902)

This commit is contained in:
Takashi Kokubun 2026-07-16 10:46:57 -07:00 committed by GitHub
parent 2fd3c2edea
commit 61441f2d72
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
Notes: git 2026-07-16 17:47:26 +00:00
Merged-By: k0kubun <takashikkbn@gmail.com>
3 changed files with 25 additions and 12 deletions

View File

@ -213,7 +213,7 @@ pub extern "C" fn rb_zjit_iseq_gen_entry_point(iseq: IseqPtr, ec: EcPtr, jit_exc
fn gen_iseq_entry_point(cb: &mut CodeBlock, iseq: IseqPtr, jit_exception: bool) -> Result<CodePtr, CompileError> {
// We don't support exception handlers yet
if jit_exception {
return Err(CompileError::ExceptionHandler);
return gen_exception_handler_counter(cb);
}
let iseq_name = iseq_get_location(iseq, 0);
@ -525,7 +525,6 @@ fn gen_function(cb: &mut CodeBlock, iseq: IseqPtr, version: IseqVersionRef, func
debug!("ZJIT: gen_function: Failed to compile insn: {insn_id} {insn}. Generating side-exit.");
gen_incr_counter(&mut asm, exit_counter_for_unhandled_hir_insn(&insn));
let reason = match insn {
Insn::Throw { .. } => SideExitReason::UnhandledHIRThrow,
Insn::InvokeBuiltin { .. } => SideExitReason::UnhandledHIRInvokeBuiltin,
_ => SideExitReason::UnhandledHIRUnknown(insn_id),
};
@ -784,7 +783,7 @@ fn gen_insn(cb: &mut CodeBlock, jit: &mut JITState, asm: &mut Assembler, functio
&Insn::IsA { val, class } => gen_is_a(jit, asm, opnd!(val), opnd!(class)),
&Insn::ArrayMax { ref elements, state } => gen_array_max(jit, asm, function, opnds!(elements), &function.frame_state(state)),
&Insn::ArrayMin { ref elements, state } => gen_array_min(jit, asm, function, opnds!(elements), &function.frame_state(state)),
&Insn::Throw { state, .. } => return Err(state),
&Insn::Throw { state, .. } => no_output!(gen_throw(jit, asm, function, &function.frame_state(state))),
&Insn::CondBranch { .. }
| &Insn::Jump { .. } | Insn::Entries { .. } => unreachable!(),
};
@ -2582,6 +2581,12 @@ fn gen_return(asm: &mut Assembler, val: lir::Opnd) {
asm.cret(C_RET_OPND);
}
fn gen_throw(jit: &mut JITState, asm: &mut Assembler, function: &Function, state: &FrameState) {
// TODO: Consider calling rb_vm_throw and propagating ec->tag->state to the interpreter.
// Also consider making it a jump on method inlining.
gen_side_exit(jit, asm, function, &SideExitReason::Throw, None, state);
}
/// Compile Fixnum + Fixnum
fn gen_fixnum_add(jit: &mut JITState, asm: &mut Assembler, function: &Function, left: lir::Opnd, right: lir::Opnd, state: &FrameState) -> lir::Opnd {
// Add left + right and test for overflow
@ -3971,6 +3976,19 @@ fn gen_compile_error_counter(cb: &mut CodeBlock, compile_error: &CompileError) -
})
}
/// Generate a JIT entry that just increments exit_exception_handler and exits
fn gen_exception_handler_counter(cb: &mut CodeBlock) -> Result<CodePtr, CompileError> {
let mut asm = Assembler::new();
asm.new_block_without_id("exception_handler_counter");
gen_incr_counter(&mut asm, Counter::exit_exception_handler);
asm.cret(Qundef.into());
asm.compile(cb).map(|(code_ptr, gc_offsets)| {
assert_eq!(0, gc_offsets.len());
code_ptr
})
}
/// Given the number of spill slots needed for a function, return the number of bytes
/// the function needs to allocate on the stack for the stack frame.
fn aligned_stack_bytes(num_slots: usize) -> usize {

View File

@ -553,6 +553,7 @@ pub enum SideExitReason {
PatchPoint(Invariant),
CalleeSideExit,
Interrupt,
Throw,
BlockParamProxyNotIseqOrIfunc,
BlockParamProxyNotNil,
BlockParamProxyNotProc,

View File

@ -185,6 +185,7 @@ make_counters! {
exit {
// exit_: Side exits reasons
exit_compile_error,
exit_exception_handler,
exit_unhandled_newarray_send_min,
exit_unhandled_newarray_send_hash,
exit_unhandled_newarray_send_pack,
@ -226,6 +227,7 @@ make_counters! {
exit_patchpoint_root_box_only,
exit_callee_side_exit,
exit_interrupt,
exit_throw,
exit_stackoverflow,
exit_block_param_proxy_not_iseq_or_ifunc,
exit_block_param_proxy_not_nil,
@ -349,7 +351,6 @@ make_counters! {
compile_error_iseq_version_limit_reached,
compile_error_iseq_stack_too_large,
compile_error_native_stack_too_large,
compile_error_exception_handler,
compile_error_out_of_memory,
compile_error_label_linking_failure,
compile_error_jit_to_jit_optional,
@ -369,9 +370,6 @@ make_counters! {
compile_error_validation_misc_validation_error,
// unhandled_hir_insn_: Unhandled HIR instructions
unhandled_hir_insn_array_max,
unhandled_hir_insn_fixnum_div,
unhandled_hir_insn_throw,
unhandled_hir_insn_invokebuiltin,
unhandled_hir_insn_unknown,
@ -523,7 +521,6 @@ pub enum CompileError {
IseqVersionLimitReached,
IseqStackTooLarge,
NativeStackTooLarge,
ExceptionHandler,
OutOfMemory,
ParseError(ParseError),
/// When a ZJIT function is too large, the branches may have
@ -542,7 +539,6 @@ pub fn exit_counter_for_compile_error(compile_error: &CompileError) -> Counter {
IseqVersionLimitReached => compile_error_iseq_version_limit_reached,
IseqStackTooLarge => compile_error_iseq_stack_too_large,
NativeStackTooLarge => compile_error_native_stack_too_large,
ExceptionHandler => compile_error_exception_handler,
OutOfMemory => compile_error_out_of_memory,
LabelLinkingFailure => compile_error_label_linking_failure,
ParseError(parse_error) => match parse_error {
@ -568,9 +564,6 @@ pub fn exit_counter_for_unhandled_hir_insn(insn: &crate::hir::Insn) -> Counter {
use crate::hir::Insn::*;
use crate::stats::Counter::*;
match insn {
ArrayMax { .. } => unhandled_hir_insn_array_max,
FixnumDiv { .. } => unhandled_hir_insn_fixnum_div,
Throw { .. } => unhandled_hir_insn_throw,
InvokeBuiltin { .. } => unhandled_hir_insn_invokebuiltin,
_ => unhandled_hir_insn_unknown,
}
@ -617,6 +610,7 @@ pub fn side_exit_counter(reason: crate::hir::SideExitReason) -> Counter {
GuardSuperMethodEntry => exit_guard_super_method_entry,
CalleeSideExit => exit_callee_side_exit,
Interrupt => exit_interrupt,
Throw => exit_throw,
StackOverflow => exit_stackoverflow,
BlockParamProxyNotIseqOrIfunc => exit_block_param_proxy_not_iseq_or_ifunc,
BlockParamProxyNotNil => exit_block_param_proxy_not_nil,