Skip to content

Commit 99bf47a

Browse files
committed
ZJIT: Report stats for unhandled call types
1 parent 710c5c8 commit 99bf47a

4 files changed

Lines changed: 117 additions & 78 deletions

File tree

zjit.rb

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ def stats_string
3535
stats = self.stats
3636

3737
print_counters_with_prefix(prefix: 'failed_', prompt: 'compilation failure reasons', buf:, stats:)
38+
print_counters_with_prefix(prefix: 'unhandled_call_', prompt: 'unhandled call types', buf:, stats:, limit: 20)
3839
print_counters([
3940
:dynamic_send_count,
4041

zjit/src/backend/lir.rs

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ use crate::cruby::{Qundef, RUBY_OFFSET_CFP_PC, RUBY_OFFSET_CFP_SP, SIZEOF_VALUE_
66
use crate::hir::SideExitReason;
77
use crate::options::{debug, get_option};
88
use crate::cruby::VALUE;
9-
use crate::stats::{exit_counter_ptr, exit_counter_ptr_for_opcode};
9+
use crate::stats::{exit_counter_ptr, exit_counter_ptr_for_opcode, exit_counter_ptr_for_call_type};
1010
use crate::virtualmem::CodePtr;
1111
use crate::asm::{CodeBlock, Label};
1212

@@ -1604,6 +1604,11 @@ impl Assembler
16041604
self.load_into(SCRATCH_OPND, Opnd::const_ptr(exit_counter_ptr_for_opcode(opcode)));
16051605
self.incr_counter_with_reg(Opnd::mem(64, SCRATCH_OPND, 0), 1.into(), C_RET_OPND);
16061606
}
1607+
if let SideExitReason::UnhandledCallType(call_type) = reason {
1608+
asm_comment!(self, "increment an unknown call type counter");
1609+
self.load_into(SCRATCH_OPND, Opnd::const_ptr(exit_counter_ptr_for_call_type(call_type)));
1610+
self.incr_counter_with_reg(Opnd::mem(64, SCRATCH_OPND, 0), 1.into(), C_RET_OPND);
1611+
}
16071612
}
16081613

16091614
asm_comment!(self, "exit to the interpreter");

zjit/src/hir.rs

Lines changed: 75 additions & 75 deletions
Original file line numberDiff line numberDiff line change
@@ -439,7 +439,7 @@ impl PtrPrintMap {
439439
#[derive(Debug, Clone, Copy)]
440440
pub enum SideExitReason {
441441
UnknownNewarraySend(vm_opt_newarray_send_type),
442-
UnknownCallType,
442+
UnhandledCallType(CallType),
443443
UnknownSpecialVariable(u64),
444444
UnhandledHIRInsn(InsnId),
445445
UnhandledYARVInsn(u32),
@@ -2808,7 +2808,7 @@ fn compute_bytecode_info(iseq: *const rb_iseq_t) -> BytecodeInfo {
28082808
BytecodeInfo { jump_targets: result, has_send }
28092809
}
28102810

2811-
#[derive(Debug, PartialEq)]
2811+
#[derive(Debug, PartialEq, Clone, Copy)]
28122812
pub enum CallType {
28132813
Splat,
28142814
BlockArg,
@@ -2846,19 +2846,19 @@ fn num_locals(iseq: *const rb_iseq_t) -> usize {
28462846
}
28472847

28482848
/// If we can't handle the type of send (yet), bail out.
2849-
fn unknown_call_type(flag: u32) -> bool {
2850-
if (flag & VM_CALL_KW_SPLAT_MUT) != 0 { return true; }
2851-
if (flag & VM_CALL_ARGS_SPLAT_MUT) != 0 { return true; }
2852-
if (flag & VM_CALL_ARGS_SPLAT) != 0 { return true; }
2853-
if (flag & VM_CALL_KW_SPLAT) != 0 { return true; }
2854-
if (flag & VM_CALL_ARGS_BLOCKARG) != 0 { return true; }
2855-
if (flag & VM_CALL_KWARG) != 0 { return true; }
2856-
if (flag & VM_CALL_TAILCALL) != 0 { return true; }
2857-
if (flag & VM_CALL_SUPER) != 0 { return true; }
2858-
if (flag & VM_CALL_ZSUPER) != 0 { return true; }
2859-
if (flag & VM_CALL_OPT_SEND) != 0 { return true; }
2860-
if (flag & VM_CALL_FORWARDING) != 0 { return true; }
2861-
false
2849+
fn unknown_call_type(flag: u32) -> Result<(), CallType> {
2850+
if (flag & VM_CALL_KW_SPLAT_MUT) != 0 { return Err(CallType::KwSplatMut); }
2851+
if (flag & VM_CALL_ARGS_SPLAT_MUT) != 0 { return Err(CallType::SplatMut); }
2852+
if (flag & VM_CALL_ARGS_SPLAT) != 0 { return Err(CallType::Splat); }
2853+
if (flag & VM_CALL_KW_SPLAT) != 0 { return Err(CallType::KwSplat); }
2854+
if (flag & VM_CALL_ARGS_BLOCKARG) != 0 { return Err(CallType::BlockArg); }
2855+
if (flag & VM_CALL_KWARG) != 0 { return Err(CallType::Kwarg); }
2856+
if (flag & VM_CALL_TAILCALL) != 0 { return Err(CallType::Tailcall); }
2857+
if (flag & VM_CALL_SUPER) != 0 { return Err(CallType::Super); }
2858+
if (flag & VM_CALL_ZSUPER) != 0 { return Err(CallType::Zsuper); }
2859+
if (flag & VM_CALL_OPT_SEND) != 0 { return Err(CallType::OptSend); }
2860+
if (flag & VM_CALL_FORWARDING) != 0 { return Err(CallType::Forwarding); }
2861+
Ok(())
28622862
}
28632863

28642864
/// We have IseqPayload, which keeps track of HIR Types in the interpreter, but this is not useful
@@ -3324,10 +3324,10 @@ pub fn iseq_to_hir(iseq: *const rb_iseq_t) -> Result<Function, ParseError> {
33243324
// NB: opt_neq has two cd; get_arg(0) is for eq and get_arg(1) is for neq
33253325
let cd: *const rb_call_data = get_arg(pc, 1).as_ptr();
33263326
let call_info = unsafe { rb_get_call_data_ci(cd) };
3327-
if unknown_call_type(unsafe { rb_vm_ci_flag(call_info) }) {
3327+
if let Err(call_type) = unknown_call_type(unsafe { rb_vm_ci_flag(call_info) }) {
33283328
// Unknown call type; side-exit into the interpreter
33293329
let exit_id = fun.push_insn(block, Insn::Snapshot { state: exit_state });
3330-
fun.push_insn(block, Insn::SideExit { state: exit_id, reason: SideExitReason::UnknownCallType });
3330+
fun.push_insn(block, Insn::SideExit { state: exit_id, reason: SideExitReason::UnhandledCallType(call_type) });
33313331
break; // End the block
33323332
}
33333333
let argc = unsafe { vm_ci_argc((*cd).ci) };
@@ -3345,10 +3345,10 @@ pub fn iseq_to_hir(iseq: *const rb_iseq_t) -> Result<Function, ParseError> {
33453345
// NB: these instructions have the recv for the call at get_arg(0)
33463346
let cd: *const rb_call_data = get_arg(pc, 1).as_ptr();
33473347
let call_info = unsafe { rb_get_call_data_ci(cd) };
3348-
if unknown_call_type(unsafe { rb_vm_ci_flag(call_info) }) {
3348+
if let Err(call_type) = unknown_call_type(unsafe { rb_vm_ci_flag(call_info) }) {
33493349
// Unknown call type; side-exit into the interpreter
33503350
let exit_id = fun.push_insn(block, Insn::Snapshot { state: exit_state });
3351-
fun.push_insn(block, Insn::SideExit { state: exit_id, reason: SideExitReason::UnknownCallType });
3351+
fun.push_insn(block, Insn::SideExit { state: exit_id, reason: SideExitReason::UnhandledCallType(call_type) });
33523352
break; // End the block
33533353
}
33543354
let argc = unsafe { vm_ci_argc((*cd).ci) };
@@ -3403,10 +3403,10 @@ pub fn iseq_to_hir(iseq: *const rb_iseq_t) -> Result<Function, ParseError> {
34033403
YARVINSN_opt_send_without_block => {
34043404
let cd: *const rb_call_data = get_arg(pc, 0).as_ptr();
34053405
let call_info = unsafe { rb_get_call_data_ci(cd) };
3406-
if unknown_call_type(unsafe { rb_vm_ci_flag(call_info) }) {
3406+
if let Err(call_type) = unknown_call_type(unsafe { rb_vm_ci_flag(call_info) }) {
34073407
// Unknown call type; side-exit into the interpreter
34083408
let exit_id = fun.push_insn(block, Insn::Snapshot { state: exit_state });
3409-
fun.push_insn(block, Insn::SideExit { state: exit_id, reason: SideExitReason::UnknownCallType });
3409+
fun.push_insn(block, Insn::SideExit { state: exit_id, reason: SideExitReason::UnhandledCallType(call_type) });
34103410
break; // End the block
34113411
}
34123412
let argc = unsafe { vm_ci_argc((*cd).ci) };
@@ -3421,10 +3421,10 @@ pub fn iseq_to_hir(iseq: *const rb_iseq_t) -> Result<Function, ParseError> {
34213421
let cd: *const rb_call_data = get_arg(pc, 0).as_ptr();
34223422
let blockiseq: IseqPtr = get_arg(pc, 1).as_iseq();
34233423
let call_info = unsafe { rb_get_call_data_ci(cd) };
3424-
if unknown_call_type(unsafe { rb_vm_ci_flag(call_info) }) {
3424+
if let Err(call_type) = unknown_call_type(unsafe { rb_vm_ci_flag(call_info) }) {
34253425
// Unknown call type; side-exit into the interpreter
34263426
let exit_id = fun.push_insn(block, Insn::Snapshot { state: exit_state });
3427-
fun.push_insn(block, Insn::SideExit { state: exit_id, reason: SideExitReason::UnknownCallType });
3427+
fun.push_insn(block, Insn::SideExit { state: exit_id, reason: SideExitReason::UnhandledCallType(call_type) });
34283428
break; // End the block
34293429
}
34303430
let argc = unsafe { vm_ci_argc((*cd).ci) };
@@ -3549,8 +3549,8 @@ pub fn iseq_to_hir(iseq: *const rb_iseq_t) -> Result<Function, ParseError> {
35493549
let cd: *const rb_call_data = get_arg(pc, 0).as_ptr();
35503550
let call_info = unsafe { rb_get_call_data_ci(cd) };
35513551

3552-
if unknown_call_type(unsafe { rb_vm_ci_flag(call_info) }) {
3553-
assert!(false, "objtostring should not have unknown call type");
3552+
if let Err(call_type) = unknown_call_type(unsafe { rb_vm_ci_flag(call_info) }) {
3553+
assert!(false, "objtostring should not have unknown call type {call_type:?}");
35543554
}
35553555
let argc = unsafe { vm_ci_argc((*cd).ci) };
35563556
assert_eq!(0, argc, "objtostring should not have args");
@@ -4872,12 +4872,12 @@ mod tests {
48724872
eval("
48734873
def test(a) = foo(*a)
48744874
");
4875-
assert_snapshot!(hir_string("test"), @r#"
4876-
fn test@<compiled>:2:
4877-
bb0(v0:BasicObject, v1:BasicObject):
4878-
v4:ArrayExact = ToArray v1
4879-
SideExit UnknownCallType
4880-
"#);
4875+
assert_snapshot!(hir_string("test"), @r"
4876+
fn test@<compiled>:2:
4877+
bb0(v0:BasicObject, v1:BasicObject):
4878+
v4:ArrayExact = ToArray v1
4879+
SideExit UnhandledCallType(Splat)
4880+
");
48814881
}
48824882

48834883
#[test]
@@ -4889,7 +4889,7 @@ mod tests {
48894889
fn test@<compiled>:2:
48904890
bb0(v0:BasicObject, v1:BasicObject):
48914891
v3:BasicObject = GetLocal l0, EP@3
4892-
SideExit UnknownCallType
4892+
SideExit UnhandledCallType(BlockArg)
48934893
");
48944894
}
48954895

@@ -4898,24 +4898,24 @@ mod tests {
48984898
eval("
48994899
def test(a) = foo(a: 1)
49004900
");
4901-
assert_snapshot!(hir_string("test"), @r#"
4902-
fn test@<compiled>:2:
4903-
bb0(v0:BasicObject, v1:BasicObject):
4904-
v3:Fixnum[1] = Const Value(1)
4905-
SideExit UnknownCallType
4906-
"#);
4901+
assert_snapshot!(hir_string("test"), @r"
4902+
fn test@<compiled>:2:
4903+
bb0(v0:BasicObject, v1:BasicObject):
4904+
v3:Fixnum[1] = Const Value(1)
4905+
SideExit UnhandledCallType(Kwarg)
4906+
");
49074907
}
49084908

49094909
#[test]
49104910
fn test_cant_compile_kw_splat() {
49114911
eval("
49124912
def test(a) = foo(**a)
49134913
");
4914-
assert_snapshot!(hir_string("test"), @r#"
4915-
fn test@<compiled>:2:
4916-
bb0(v0:BasicObject, v1:BasicObject):
4917-
SideExit UnknownCallType
4918-
"#);
4914+
assert_snapshot!(hir_string("test"), @r"
4915+
fn test@<compiled>:2:
4916+
bb0(v0:BasicObject, v1:BasicObject):
4917+
SideExit UnhandledCallType(KwSplat)
4918+
");
49194919
}
49204920

49214921
// TODO(max): Figure out how to generate a call with TAILCALL flag
@@ -4965,33 +4965,33 @@ mod tests {
49654965
eval("
49664966
def test(a) = foo **a, b: 1
49674967
");
4968-
assert_snapshot!(hir_string("test"), @r#"
4969-
fn test@<compiled>:2:
4970-
bb0(v0:BasicObject, v1:BasicObject):
4971-
v3:Class[VMFrozenCore] = Const Value(VALUE(0x1000))
4972-
v5:HashExact = NewHash
4973-
v7:BasicObject = SendWithoutBlock v3, :core#hash_merge_kwd, v5, v1
4974-
v8:Class[VMFrozenCore] = Const Value(VALUE(0x1000))
4975-
v9:StaticSymbol[:b] = Const Value(VALUE(0x1008))
4976-
v10:Fixnum[1] = Const Value(1)
4977-
v12:BasicObject = SendWithoutBlock v8, :core#hash_merge_ptr, v7, v9, v10
4978-
SideExit UnknownCallType
4979-
"#);
4968+
assert_snapshot!(hir_string("test"), @r"
4969+
fn test@<compiled>:2:
4970+
bb0(v0:BasicObject, v1:BasicObject):
4971+
v3:Class[VMFrozenCore] = Const Value(VALUE(0x1000))
4972+
v5:HashExact = NewHash
4973+
v7:BasicObject = SendWithoutBlock v3, :core#hash_merge_kwd, v5, v1
4974+
v8:Class[VMFrozenCore] = Const Value(VALUE(0x1000))
4975+
v9:StaticSymbol[:b] = Const Value(VALUE(0x1008))
4976+
v10:Fixnum[1] = Const Value(1)
4977+
v12:BasicObject = SendWithoutBlock v8, :core#hash_merge_ptr, v7, v9, v10
4978+
SideExit UnhandledCallType(KwSplatMut)
4979+
");
49804980
}
49814981

49824982
#[test]
49834983
fn test_cant_compile_splat_mut() {
49844984
eval("
49854985
def test(*) = foo *, 1
49864986
");
4987-
assert_snapshot!(hir_string("test"), @r#"
4988-
fn test@<compiled>:2:
4989-
bb0(v0:BasicObject, v1:ArrayExact):
4990-
v4:ArrayExact = ToNewArray v1
4991-
v5:Fixnum[1] = Const Value(1)
4992-
ArrayPush v4, v5
4993-
SideExit UnknownCallType
4994-
"#);
4987+
assert_snapshot!(hir_string("test"), @r"
4988+
fn test@<compiled>:2:
4989+
bb0(v0:BasicObject, v1:ArrayExact):
4990+
v4:ArrayExact = ToNewArray v1
4991+
v5:Fixnum[1] = Const Value(1)
4992+
ArrayPush v4, v5
4993+
SideExit UnhandledCallType(SplatMut)
4994+
");
49954995
}
49964996

49974997
#[test]
@@ -7398,12 +7398,12 @@ mod opt_tests {
73987398
test
73997399
test
74007400
");
7401-
assert_snapshot!(hir_string("test"), @r#"
7402-
fn test@<compiled>:3:
7403-
bb0(v0:BasicObject):
7404-
v2:Fixnum[1] = Const Value(1)
7405-
SideExit UnknownCallType
7406-
"#);
7401+
assert_snapshot!(hir_string("test"), @r"
7402+
fn test@<compiled>:3:
7403+
bb0(v0:BasicObject):
7404+
v2:Fixnum[1] = Const Value(1)
7405+
SideExit UnhandledCallType(Kwarg)
7406+
");
74077407
}
74087408

74097409
#[test]
@@ -7414,12 +7414,12 @@ mod opt_tests {
74147414
test
74157415
test
74167416
");
7417-
assert_snapshot!(hir_string("test"), @r#"
7418-
fn test@<compiled>:3:
7419-
bb0(v0:BasicObject):
7420-
v2:Fixnum[1] = Const Value(1)
7421-
SideExit UnknownCallType
7422-
"#);
7417+
assert_snapshot!(hir_string("test"), @r"
7418+
fn test@<compiled>:3:
7419+
bb0(v0:BasicObject):
7420+
v2:Fixnum[1] = Const Value(1)
7421+
SideExit UnhandledCallType(Kwarg)
7422+
");
74237423
}
74247424

74257425
#[test]

zjit/src/stats.rs

Lines changed: 35 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,7 @@ make_counters! {
8585
// exit_: Side exits reasons
8686
exit_compilation_failure,
8787
exit_unknown_newarray_send,
88-
exit_unknown_call_type,
88+
exit_unhandled_call_type,
8989
exit_unknown_special_variable,
9090
exit_unhandled_hir_insn,
9191
exit_unhandled_yarv_insn,
@@ -101,6 +101,19 @@ make_counters! {
101101
exit_interrupt,
102102
}
103103

104+
// unhanded_call_: Unhandled call types
105+
unhandled_call_splat,
106+
unhandled_call_block_arg,
107+
unhandled_call_kwarg,
108+
unhandled_call_kw_splat,
109+
unhandled_call_tailcall,
110+
unhandled_call_super,
111+
unhandled_call_zsuper,
112+
unhandled_call_optsend,
113+
unhandled_call_kw_splat_mut,
114+
unhandled_call_splat_mut,
115+
unhandled_call_forwarding,
116+
104117
// failed_: Compilation failure reasons
105118
failed_iseq_stack_too_large,
106119
failed_hir_compile,
@@ -138,12 +151,32 @@ pub fn exit_counter_ptr_for_opcode(opcode: u32) -> *mut u64 {
138151
unsafe { exit_counters.get_unchecked_mut(opcode as usize) }
139152
}
140153

154+
/// Return a raw pointer to the exit counter for a given call type
155+
pub fn exit_counter_ptr_for_call_type(call_type: crate::hir::CallType) -> *mut u64 {
156+
use crate::hir::CallType::*;
157+
use crate::stats::Counter::*;
158+
let counter = match call_type {
159+
Splat => unhandled_call_splat,
160+
BlockArg => unhandled_call_block_arg,
161+
Kwarg => unhandled_call_kwarg,
162+
KwSplat => unhandled_call_kw_splat,
163+
Tailcall => unhandled_call_tailcall,
164+
Super => unhandled_call_super,
165+
Zsuper => unhandled_call_zsuper,
166+
OptSend => unhandled_call_optsend,
167+
KwSplatMut => unhandled_call_kw_splat_mut,
168+
SplatMut => unhandled_call_splat_mut,
169+
Forwarding => unhandled_call_forwarding,
170+
};
171+
counter_ptr(counter)
172+
}
173+
141174
pub fn exit_counter_ptr(reason: crate::hir::SideExitReason) -> *mut u64 {
142175
use crate::hir::SideExitReason::*;
143176
use crate::stats::Counter::*;
144177
let counter = match reason {
145178
UnknownNewarraySend(_) => exit_unknown_newarray_send,
146-
UnknownCallType => exit_unknown_call_type,
179+
UnhandledCallType(_) => exit_unhandled_call_type,
147180
UnknownSpecialVariable(_) => exit_unknown_special_variable,
148181
UnhandledHIRInsn(_) => exit_unhandled_hir_insn,
149182
UnhandledYARVInsn(_) => exit_unhandled_yarv_insn,

0 commit comments

Comments
 (0)