diff --git a/zjit/src/codegen.rs b/zjit/src/codegen.rs index 1293957d7e..a04061f06d 100644 --- a/zjit/src/codegen.rs +++ b/zjit/src/codegen.rs @@ -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 { // 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 { + 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 { diff --git a/zjit/src/hir.rs b/zjit/src/hir.rs index 9c63a5fb0c..ba35d3e5e7 100644 --- a/zjit/src/hir.rs +++ b/zjit/src/hir.rs @@ -553,6 +553,7 @@ pub enum SideExitReason { PatchPoint(Invariant), CalleeSideExit, Interrupt, + Throw, BlockParamProxyNotIseqOrIfunc, BlockParamProxyNotNil, BlockParamProxyNotProc, diff --git a/zjit/src/stats.rs b/zjit/src/stats.rs index 6a05e5f507..5f964a2734 100644 --- a/zjit/src/stats.rs +++ b/zjit/src/stats.rs @@ -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,