diff options
| -rw-r--r-- | zjit.rb | 1 | ||||
| -rw-r--r-- | zjit/src/codegen.rs | 14 | ||||
| -rw-r--r-- | zjit/src/hir.rs | 60 | ||||
| -rw-r--r-- | zjit/src/stats.rs | 34 |
4 files changed, 98 insertions, 11 deletions
@@ -44,6 +44,7 @@ class << RubyVM::ZJIT print_counters_with_prefix(prefix: 'compile_error_', prompt: 'compile error reasons', buf:, stats:, limit: 20) print_counters_with_prefix(prefix: 'exit_', prompt: 'side exit reasons', buf:, stats:, limit: 20) print_counters_with_prefix(prefix: 'dynamic_send_type_', prompt: 'dynamic send types', buf:, stats:, limit: 20) + print_counters_with_prefix(prefix: 'send_fallback_', prompt: 'send fallback def_types', buf:, stats:, limit: 20) # Show the most important stats ratio_in_zjit at the end print_counters([ diff --git a/zjit/src/codegen.rs b/zjit/src/codegen.rs index 12bd6d4aa3..fce16b5d93 100644 --- a/zjit/src/codegen.rs +++ b/zjit/src/codegen.rs @@ -13,10 +13,10 @@ use crate::invariants::{track_bop_assumption, track_cme_assumption, track_no_ep_ use crate::gc::{append_gc_offsets, get_or_create_iseq_payload, get_or_create_iseq_payload_ptr, IseqPayload, IseqStatus}; use crate::state::ZJITState; use crate::stats::{exit_counter_for_compile_error, incr_counter, incr_counter_by, CompileError}; -use crate::stats::{counter_ptr, with_time_stat, Counter, Counter::{compile_time_ns, exit_compile_error}}; +use crate::stats::{counter_ptr, with_time_stat, Counter, send_fallback_counter, Counter::{compile_time_ns, exit_compile_error}}; use crate::{asm::CodeBlock, cruby::*, options::debug, virtualmem::CodePtr}; use crate::backend::lir::{self, asm_comment, asm_ccall, Assembler, Opnd, Target, CFP, C_ARG_OPNDS, C_RET_OPND, EC, NATIVE_STACK_PTR, NATIVE_BASE_PTR, SCRATCH_OPND, SP}; -use crate::hir::{iseq_to_hir, Block, BlockId, BranchEdge, Invariant, RangeType, SideExitReason, SideExitReason::*, SpecialObjectType, SpecialBackrefSymbol, SELF_PARAM_IDX}; +use crate::hir::{iseq_to_hir, Block, BlockId, BranchEdge, Invariant, RangeType, SideExitReason, SideExitReason::*, MethodType, SpecialObjectType, SpecialBackrefSymbol, SELF_PARAM_IDX}; use crate::hir::{Const, FrameState, Function, Insn, InsnId}; use crate::hir_type::{types, Type}; use crate::options::get_option; @@ -364,10 +364,10 @@ fn gen_insn(cb: &mut CodeBlock, jit: &mut JITState, asm: &mut Assembler, functio Insn::IfTrue { val, target } => no_output!(gen_if_true(jit, asm, opnd!(val), target)), Insn::IfFalse { val, target } => no_output!(gen_if_false(jit, asm, opnd!(val), target)), &Insn::Send { cd, blockiseq, state, .. } => gen_send(jit, asm, cd, blockiseq, &function.frame_state(state)), - Insn::SendWithoutBlock { cd, state, .. } => gen_send_without_block(jit, asm, *cd, &function.frame_state(*state)), + Insn::SendWithoutBlock { cd, state, def_type, .. } => gen_send_without_block(jit, asm, *cd, *def_type, &function.frame_state(*state)), // Give up SendWithoutBlockDirect for 6+ args since asm.ccall() doesn't support it. Insn::SendWithoutBlockDirect { cd, state, args, .. } if args.len() + 1 > C_ARG_OPNDS.len() => // +1 for self - gen_send_without_block(jit, asm, *cd, &function.frame_state(*state)), + gen_send_without_block(jit, asm, *cd, None, &function.frame_state(*state)), Insn::SendWithoutBlockDirect { cme, iseq, recv, args, state, .. } => gen_send_without_block_direct(cb, jit, asm, *cme, *iseq, opnd!(recv), opnds!(args), &function.frame_state(*state)), &Insn::InvokeSuper { cd, blockiseq, state, .. } => gen_invokesuper(jit, asm, cd, blockiseq, &function.frame_state(state)), Insn::InvokeBlock { cd, state, .. } => gen_invokeblock(jit, asm, *cd, &function.frame_state(*state)), @@ -999,10 +999,16 @@ fn gen_send_without_block( jit: &mut JITState, asm: &mut Assembler, cd: *const rb_call_data, + def_type: Option<MethodType>, state: &FrameState, ) -> lir::Opnd { gen_incr_counter(asm, Counter::dynamic_send_count); gen_incr_counter(asm, Counter::dynamic_send_type_send_without_block); + + if let Some(def_type) = def_type { + gen_incr_counter(asm, send_fallback_counter(def_type)); + } + gen_prepare_non_leaf_call(jit, asm, state); asm_comment!(asm, "call #{} with dynamic dispatch", ruby_call_method_name(cd)); unsafe extern "C" { diff --git a/zjit/src/hir.rs b/zjit/src/hir.rs index 033531fd56..c7b0f0d610 100644 --- a/zjit/src/hir.rs +++ b/zjit/src/hir.rs @@ -467,6 +467,42 @@ pub enum SideExitReason { StackOverflow, } +#[derive(Debug, Clone, Copy)] +pub enum MethodType { + Iseq, + Cfunc, + Attrset, + Ivar, + Bmethod, + Zsuper, + Alias, + Undefined, + NotImplemented, + Optimized, + Missing, + Refined, +} + +impl From<u32> for MethodType { + fn from(value: u32) -> Self { + match value { + VM_METHOD_TYPE_ISEQ => MethodType::Iseq, + VM_METHOD_TYPE_CFUNC => MethodType::Cfunc, + VM_METHOD_TYPE_ATTRSET => MethodType::Attrset, + VM_METHOD_TYPE_IVAR => MethodType::Ivar, + VM_METHOD_TYPE_BMETHOD => MethodType::Bmethod, + VM_METHOD_TYPE_ZSUPER => MethodType::Zsuper, + VM_METHOD_TYPE_ALIAS => MethodType::Alias, + VM_METHOD_TYPE_UNDEF => MethodType::Undefined, + VM_METHOD_TYPE_NOTIMPLEMENTED => MethodType::NotImplemented, + VM_METHOD_TYPE_OPTIMIZED => MethodType::Optimized, + VM_METHOD_TYPE_MISSING => MethodType::Missing, + VM_METHOD_TYPE_REFINED => MethodType::Refined, + _ => unreachable!("unknown send_without_block def_type: {}", value), + } + } +} + impl std::fmt::Display for SideExitReason { fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { match self { @@ -583,7 +619,13 @@ pub enum Insn { /// Un-optimized fallback implementation (dynamic dispatch) for send-ish instructions /// Ignoring keyword arguments etc for now - SendWithoutBlock { recv: InsnId, cd: *const rb_call_data, args: Vec<InsnId>, state: InsnId }, + SendWithoutBlock { + recv: InsnId, + cd: *const rb_call_data, + args: Vec<InsnId>, + def_type: Option<MethodType>, // Assigned in `optimize_direct_sends` if it's not optimized + state: InsnId, + }, Send { recv: InsnId, cd: *const rb_call_data, blockiseq: IseqPtr, args: Vec<InsnId>, state: InsnId }, InvokeSuper { recv: InsnId, cd: *const rb_call_data, blockiseq: IseqPtr, args: Vec<InsnId>, state: InsnId }, InvokeBlock { cd: *const rb_call_data, args: Vec<InsnId>, state: InsnId }, @@ -1330,10 +1372,11 @@ impl Function { str: find!(str), state, }, - &SendWithoutBlock { recv, cd, ref args, state } => SendWithoutBlock { + &SendWithoutBlock { recv, cd, ref args, def_type, state } => SendWithoutBlock { recv: find!(recv), cd, args: find_vec!(args), + def_type, state, }, &SendWithoutBlockDirect { recv, cd, cme, iseq, ref args, state } => SendWithoutBlockDirect { @@ -1742,7 +1785,7 @@ impl Function { self.try_rewrite_uminus(block, insn_id, recv, state), Insn::SendWithoutBlock { recv, args, state, cd, .. } if ruby_call_method_id(cd) == ID!(aref) && args.len() == 1 => self.try_rewrite_aref(block, insn_id, recv, args[0], state), - Insn::SendWithoutBlock { mut recv, cd, args, state } => { + Insn::SendWithoutBlock { mut recv, cd, args, state, .. } => { let frame_state = self.frame_state(state); let (klass, profiled_type) = if let Some(klass) = self.type_of(recv).runtime_exact_ruby_class() { // If we know the class statically, use it to fold the lookup at compile-time. @@ -1798,6 +1841,9 @@ impl Function { let getivar = self.push_insn(block, Insn::GetIvar { self_val: recv, id, state }); self.make_equal_to(insn_id, getivar); } else { + if let Insn::SendWithoutBlock { def_type: insn_def_type, .. } = &mut self.insns[insn_id.0] { + *insn_def_type = Some(MethodType::from(def_type)); + } self.push_insn_id(block, insn_id); continue; } } @@ -1839,7 +1885,7 @@ impl Function { self.make_equal_to(insn_id, guard); } else { self.push_insn(block, Insn::GuardTypeNot { val, guard_type: types::String, state}); - let send_to_s = self.push_insn(block, Insn::SendWithoutBlock { recv: val, cd, args: vec![], state}); + let send_to_s = self.push_insn(block, Insn::SendWithoutBlock { recv: val, cd, args: vec![], def_type: None, state}); self.make_equal_to(insn_id, send_to_s); } } @@ -3520,7 +3566,7 @@ pub fn iseq_to_hir(iseq: *const rb_iseq_t) -> Result<Function, ParseError> { let args = state.stack_pop_n(argc as usize)?; let recv = state.stack_pop()?; let exit_id = fun.push_insn(block, Insn::Snapshot { state: exit_state }); - let send = fun.push_insn(block, Insn::SendWithoutBlock { recv, cd, args, state: exit_id }); + let send = fun.push_insn(block, Insn::SendWithoutBlock { recv, cd, args, def_type: None, state: exit_id }); state.stack_push(send); } YARVINSN_opt_hash_freeze | @@ -3544,7 +3590,7 @@ pub fn iseq_to_hir(iseq: *const rb_iseq_t) -> Result<Function, ParseError> { let exit_id = fun.push_insn(block, Insn::Snapshot { state: exit_state }); let recv = fun.push_insn(block, Insn::Const { val: Const::Value(get_arg(pc, 0)) }); - let send = fun.push_insn(block, Insn::SendWithoutBlock { recv, cd, args, state: exit_id }); + let send = fun.push_insn(block, Insn::SendWithoutBlock { recv, cd, args, def_type: None, state: exit_id }); state.stack_push(send); } @@ -3601,7 +3647,7 @@ pub fn iseq_to_hir(iseq: *const rb_iseq_t) -> Result<Function, ParseError> { let args = state.stack_pop_n(argc as usize)?; let recv = state.stack_pop()?; let exit_id = fun.push_insn(block, Insn::Snapshot { state: exit_state }); - let send = fun.push_insn(block, Insn::SendWithoutBlock { recv, cd, args, state: exit_id }); + let send = fun.push_insn(block, Insn::SendWithoutBlock { recv, cd, args, def_type: None, state: exit_id }); state.stack_push(send); } YARVINSN_send => { diff --git a/zjit/src/stats.rs b/zjit/src/stats.rs index 18e12d3af5..1daf1fda4a 100644 --- a/zjit/src/stats.rs +++ b/zjit/src/stats.rs @@ -135,6 +135,20 @@ make_counters! { dynamic_send_type_send, dynamic_send_type_invokeblock, dynamic_send_type_invokesuper, + + // Method call def_type related to fallback to dynamic dispatch + send_fallback_iseq, + send_fallback_cfunc, + send_fallback_attrset, + send_fallback_ivar, + send_fallback_bmethod, + send_fallback_zsuper, + send_fallback_alias, + send_fallback_undef, + send_fallback_not_implemented, + send_fallback_optimized, + send_fallback_missing, + send_fallback_refined, } /// Increase a counter by a specified amount @@ -221,6 +235,26 @@ pub fn exit_counter_ptr(reason: crate::hir::SideExitReason) -> *mut u64 { counter_ptr(counter) } +pub fn send_fallback_counter(def_type: crate::hir::MethodType) -> Counter { + use crate::hir::MethodType::*; + use crate::stats::Counter::*; + + match def_type { + Iseq => send_fallback_iseq, + Cfunc => send_fallback_cfunc, + Attrset => send_fallback_attrset, + Ivar => send_fallback_ivar, + Bmethod => send_fallback_bmethod, + Zsuper => send_fallback_zsuper, + Alias => send_fallback_alias, + Undefined => send_fallback_undef, + NotImplemented => send_fallback_not_implemented, + Optimized => send_fallback_optimized, + Missing => send_fallback_missing, + Refined => send_fallback_refined, + } +} + /// Primitive called in zjit.rb. Zero out all the counters. #[unsafe(no_mangle)] pub extern "C" fn rb_zjit_reset_stats_bang(_ec: EcPtr, _self: VALUE) -> VALUE { |
