Skip to content

Commit 10c0d7a

Browse files
authored
ZJIT: Count unoptimized Send (ruby#14801)
* ZJIT: Count unoptimized `Send` This includes `Send` in `send fallback reasons` to guide future optimizations. * ZJIT: Create dedicated def_type counter for Send
1 parent e8f0e14 commit 10c0d7a

File tree

4 files changed

+114
-29
lines changed

4 files changed

+114
-29
lines changed

zjit.rb

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -159,7 +159,8 @@ def stats_string
159159
print_counters_with_prefix(prefix: 'not_annotated_cfuncs_', prompt: 'not annotated C methods', buf:, stats:, limit: 20)
160160

161161
# Show fallback counters, ordered by the typical amount of fallbacks for the prefix at the time
162-
print_counters_with_prefix(prefix: 'unspecialized_def_type_', prompt: 'not optimized method types', buf:, stats:, limit: 20)
162+
print_counters_with_prefix(prefix: 'unspecialized_send_def_type_', prompt: 'not optimized method types for send', buf:, stats:, limit: 20)
163+
print_counters_with_prefix(prefix: 'unspecialized_send_without_block_def_type_', prompt: 'not optimized method types for send_without_block', buf:, stats:, limit: 20)
163164
print_counters_with_prefix(prefix: 'not_optimized_yarv_insn_', prompt: 'not optimized instructions', buf:, stats:, limit: 20)
164165
print_counters_with_prefix(prefix: 'send_fallback_', prompt: 'send fallback reasons', buf:, stats:, limit: 20)
165166

zjit/src/codegen.rs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ use crate::invariants::{
1515
};
1616
use crate::gc::{append_gc_offsets, get_or_create_iseq_payload, get_or_create_iseq_payload_ptr, IseqCodePtrs, IseqPayload, IseqStatus};
1717
use crate::state::ZJITState;
18-
use crate::stats::{send_fallback_counter, exit_counter_for_compile_error, incr_counter, incr_counter_by, send_fallback_counter_for_method_type, send_fallback_counter_ptr_for_opcode, CompileError};
18+
use crate::stats::{send_fallback_counter, exit_counter_for_compile_error, incr_counter, incr_counter_by, send_fallback_counter_for_method_type, send_without_block_fallback_counter_for_method_type, send_fallback_counter_ptr_for_opcode, CompileError};
1919
use crate::stats::{counter_ptr, with_time_stat, Counter, Counter::{compile_time_ns, exit_compile_error}};
2020
use crate::{asm::CodeBlock, cruby::*, options::debug, virtualmem::CodePtr};
2121
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};
@@ -1617,6 +1617,9 @@ fn gen_incr_send_fallback_counter(asm: &mut Assembler, reason: SendFallbackReaso
16171617
gen_incr_counter_ptr(asm, send_fallback_counter_ptr_for_opcode(opcode));
16181618
}
16191619
SendWithoutBlockNotOptimizedMethodType(method_type) => {
1620+
gen_incr_counter(asm, send_without_block_fallback_counter_for_method_type(method_type));
1621+
}
1622+
SendNotOptimizedMethodType(method_type) => {
16201623
gen_incr_counter(asm, send_fallback_counter_for_method_type(method_type));
16211624
}
16221625
_ => {}

zjit/src/hir.rs

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -535,6 +535,9 @@ pub enum SendFallbackReason {
535535
SendWithoutBlockCfuncArrayVariadic,
536536
SendWithoutBlockNotOptimizedMethodType(MethodType),
537537
SendWithoutBlockDirectTooManyArgs,
538+
SendPolymorphic,
539+
SendNoProfiles,
540+
SendNotOptimizedMethodType(MethodType),
538541
CCallWithFrameTooManyArgs,
539542
ObjToStringNotString,
540543
/// Initial fallback reason for every instruction, which should be mutated to
@@ -2123,6 +2126,42 @@ impl Function {
21232126
self.push_insn_id(block, insn_id); continue;
21242127
}
21252128
}
2129+
// This doesn't actually optimize Send yet, just replaces the fallback reason to be more precise.
2130+
// TODO: Optimize Send
2131+
Insn::Send { recv, cd, state, .. } => {
2132+
let frame_state = self.frame_state(state);
2133+
let klass = if let Some(klass) = self.type_of(recv).runtime_exact_ruby_class() {
2134+
// If we know the class statically, use it to fold the lookup at compile-time.
2135+
klass
2136+
} else {
2137+
let Some(recv_type) = self.profiled_type_of_at(recv, frame_state.insn_idx) else {
2138+
if get_option!(stats) {
2139+
match self.is_polymorphic_at(recv, frame_state.insn_idx) {
2140+
Some(true) => self.set_dynamic_send_reason(insn_id, SendPolymorphic),
2141+
// If the class isn't known statically, then it should not also be monomorphic
2142+
Some(false) => panic!("Should not have monomorphic profile at this point in this branch"),
2143+
None => self.set_dynamic_send_reason(insn_id, SendNoProfiles),
2144+
}
2145+
}
2146+
self.push_insn_id(block, insn_id); continue;
2147+
};
2148+
recv_type.class()
2149+
};
2150+
let ci = unsafe { get_call_data_ci(cd) }; // info about the call site
2151+
let mid = unsafe { vm_ci_mid(ci) };
2152+
// Do method lookup
2153+
let mut cme = unsafe { rb_callable_method_entry(klass, mid) };
2154+
if cme.is_null() {
2155+
self.set_dynamic_send_reason(insn_id, SendNotOptimizedMethodType(MethodType::Null));
2156+
self.push_insn_id(block, insn_id); continue;
2157+
}
2158+
// Load an overloaded cme if applicable. See vm_search_cc().
2159+
// It allows you to use a faster ISEQ if possible.
2160+
cme = unsafe { rb_check_overloaded_cme(cme, ci) };
2161+
let def_type = unsafe { get_cme_def_type(cme) };
2162+
self.set_dynamic_send_reason(insn_id, SendNotOptimizedMethodType(MethodType::from(def_type)));
2163+
self.push_insn_id(block, insn_id); continue;
2164+
}
21262165
Insn::GetConstantPath { ic, state, .. } => {
21272166
let idlist: *const ID = unsafe { (*ic).segments };
21282167
let ice = unsafe { (*ic).entry };

zjit/src/stats.rs

Lines changed: 69 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -148,6 +148,9 @@ make_counters! {
148148
send_fallback_send_without_block_cfunc_array_variadic,
149149
send_fallback_send_without_block_not_optimized_method_type,
150150
send_fallback_send_without_block_direct_too_many_args,
151+
send_fallback_send_polymorphic,
152+
send_fallback_send_no_profiles,
153+
send_fallback_send_not_optimized_method_type,
151154
send_fallback_ccall_with_frame_too_many_args,
152155
send_fallback_obj_to_string_not_string,
153156
send_fallback_not_optimized_instruction,
@@ -185,20 +188,35 @@ make_counters! {
185188
dynamic_getivar_count,
186189
dynamic_setivar_count,
187190

188-
// Method call def_type related to fallback to dynamic dispatch
189-
unspecialized_def_type_iseq,
190-
unspecialized_def_type_cfunc,
191-
unspecialized_def_type_attrset,
192-
unspecialized_def_type_ivar,
193-
unspecialized_def_type_bmethod,
194-
unspecialized_def_type_zsuper,
195-
unspecialized_def_type_alias,
196-
unspecialized_def_type_undef,
197-
unspecialized_def_type_not_implemented,
198-
unspecialized_def_type_optimized,
199-
unspecialized_def_type_missing,
200-
unspecialized_def_type_refined,
201-
unspecialized_def_type_null,
191+
// Method call def_type related to send without block fallback to dynamic dispatch
192+
unspecialized_send_without_block_def_type_iseq,
193+
unspecialized_send_without_block_def_type_cfunc,
194+
unspecialized_send_without_block_def_type_attrset,
195+
unspecialized_send_without_block_def_type_ivar,
196+
unspecialized_send_without_block_def_type_bmethod,
197+
unspecialized_send_without_block_def_type_zsuper,
198+
unspecialized_send_without_block_def_type_alias,
199+
unspecialized_send_without_block_def_type_undef,
200+
unspecialized_send_without_block_def_type_not_implemented,
201+
unspecialized_send_without_block_def_type_optimized,
202+
unspecialized_send_without_block_def_type_missing,
203+
unspecialized_send_without_block_def_type_refined,
204+
unspecialized_send_without_block_def_type_null,
205+
206+
// Method call def_type related to send fallback to dynamic dispatch
207+
unspecialized_send_def_type_iseq,
208+
unspecialized_send_def_type_cfunc,
209+
unspecialized_send_def_type_attrset,
210+
unspecialized_send_def_type_ivar,
211+
unspecialized_send_def_type_bmethod,
212+
unspecialized_send_def_type_zsuper,
213+
unspecialized_send_def_type_alias,
214+
unspecialized_send_def_type_undef,
215+
unspecialized_send_def_type_not_implemented,
216+
unspecialized_send_def_type_optimized,
217+
unspecialized_send_def_type_missing,
218+
unspecialized_send_def_type_refined,
219+
unspecialized_send_def_type_null,
202220

203221
// Writes to the VM frame
204222
vm_write_pc_count,
@@ -320,30 +338,54 @@ pub fn send_fallback_counter(reason: crate::hir::SendFallbackReason) -> Counter
320338
SendWithoutBlockCfuncArrayVariadic => send_fallback_send_without_block_cfunc_array_variadic,
321339
SendWithoutBlockNotOptimizedMethodType(_) => send_fallback_send_without_block_not_optimized_method_type,
322340
SendWithoutBlockDirectTooManyArgs => send_fallback_send_without_block_direct_too_many_args,
341+
SendPolymorphic => send_fallback_send_polymorphic,
342+
SendNoProfiles => send_fallback_send_no_profiles,
343+
SendNotOptimizedMethodType(_) => send_fallback_send_not_optimized_method_type,
323344
CCallWithFrameTooManyArgs => send_fallback_ccall_with_frame_too_many_args,
324345
ObjToStringNotString => send_fallback_obj_to_string_not_string,
325346
NotOptimizedInstruction(_) => send_fallback_not_optimized_instruction,
326347
}
327348
}
328349

350+
pub fn send_without_block_fallback_counter_for_method_type(method_type: crate::hir::MethodType) -> Counter {
351+
use crate::hir::MethodType::*;
352+
use crate::stats::Counter::*;
353+
354+
match method_type {
355+
Iseq => unspecialized_send_without_block_def_type_iseq,
356+
Cfunc => unspecialized_send_without_block_def_type_cfunc,
357+
Attrset => unspecialized_send_without_block_def_type_attrset,
358+
Ivar => unspecialized_send_without_block_def_type_ivar,
359+
Bmethod => unspecialized_send_without_block_def_type_bmethod,
360+
Zsuper => unspecialized_send_without_block_def_type_zsuper,
361+
Alias => unspecialized_send_without_block_def_type_alias,
362+
Undefined => unspecialized_send_without_block_def_type_undef,
363+
NotImplemented => unspecialized_send_without_block_def_type_not_implemented,
364+
Optimized => unspecialized_send_without_block_def_type_optimized,
365+
Missing => unspecialized_send_without_block_def_type_missing,
366+
Refined => unspecialized_send_without_block_def_type_refined,
367+
Null => unspecialized_send_without_block_def_type_null,
368+
}
369+
}
370+
329371
pub fn send_fallback_counter_for_method_type(method_type: crate::hir::MethodType) -> Counter {
330372
use crate::hir::MethodType::*;
331373
use crate::stats::Counter::*;
332374

333375
match method_type {
334-
Iseq => unspecialized_def_type_iseq,
335-
Cfunc => unspecialized_def_type_cfunc,
336-
Attrset => unspecialized_def_type_attrset,
337-
Ivar => unspecialized_def_type_ivar,
338-
Bmethod => unspecialized_def_type_bmethod,
339-
Zsuper => unspecialized_def_type_zsuper,
340-
Alias => unspecialized_def_type_alias,
341-
Undefined => unspecialized_def_type_undef,
342-
NotImplemented => unspecialized_def_type_not_implemented,
343-
Optimized => unspecialized_def_type_optimized,
344-
Missing => unspecialized_def_type_missing,
345-
Refined => unspecialized_def_type_refined,
346-
Null => unspecialized_def_type_null,
376+
Iseq => unspecialized_send_def_type_iseq,
377+
Cfunc => unspecialized_send_def_type_cfunc,
378+
Attrset => unspecialized_send_def_type_attrset,
379+
Ivar => unspecialized_send_def_type_ivar,
380+
Bmethod => unspecialized_send_def_type_bmethod,
381+
Zsuper => unspecialized_send_def_type_zsuper,
382+
Alias => unspecialized_send_def_type_alias,
383+
Undefined => unspecialized_send_def_type_undef,
384+
NotImplemented => unspecialized_send_def_type_not_implemented,
385+
Optimized => unspecialized_send_def_type_optimized,
386+
Missing => unspecialized_send_def_type_missing,
387+
Refined => unspecialized_send_def_type_refined,
388+
Null => unspecialized_send_def_type_null,
347389
}
348390
}
349391

0 commit comments

Comments
 (0)