summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--zjit.rb1
-rw-r--r--zjit/src/codegen.rs14
-rw-r--r--zjit/src/hir.rs60
-rw-r--r--zjit/src/stats.rs34
4 files changed, 98 insertions, 11 deletions
diff --git a/zjit.rb b/zjit.rb
index 44bce453ff..7d2fd3a10e 100644
--- a/zjit.rb
+++ b/zjit.rb
@@ -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 {