summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMax Bernstein <max@bernsteinbear.com>2025-07-02 14:57:09 -0400
committerGitHub <noreply@github.com>2025-07-02 14:57:09 -0400
commite240b415a5286f7ec0b1edfc5a1a540c62118fd4 (patch)
treec3d8e04ffef1acb75d438599548c1ce8c1f6c02c
parent1d31c98e04098d406ae97fd51698533ab4933a0b (diff)
ZJIT: Add reason for SideExit (#13768)
This makes it clearer what is unimplemented when looking at HIR dumps.
-rw-r--r--zjit/src/codegen.rs2
-rw-r--r--zjit/src/cruby.rs3
-rw-r--r--zjit/src/hir.rs76
3 files changed, 53 insertions, 28 deletions
diff --git a/zjit/src/codegen.rs b/zjit/src/codegen.rs
index 6d73a3a32d..9fa088c0d1 100644
--- a/zjit/src/codegen.rs
+++ b/zjit/src/codegen.rs
@@ -287,7 +287,7 @@ fn gen_insn(cb: &mut CodeBlock, jit: &mut JITState, asm: &mut Assembler, functio
Insn::SetLocal { val, ep_offset, level } => return gen_nested_setlocal(asm, opnd!(val), *ep_offset, *level),
Insn::GetConstantPath { ic, state } => gen_get_constant_path(asm, *ic, &function.frame_state(*state)),
Insn::SetIvar { self_val, id, val, state: _ } => return gen_setivar(asm, opnd!(self_val), *id, opnd!(val)),
- Insn::SideExit { state } => return gen_side_exit(jit, asm, &function.frame_state(*state)),
+ Insn::SideExit { state, reason: _ } => return gen_side_exit(jit, asm, &function.frame_state(*state)),
Insn::PutSpecialObject { value_type } => gen_putspecialobject(asm, *value_type),
Insn::AnyToString { val, str, state } => gen_anytostring(asm, opnd!(val), opnd!(str), &function.frame_state(*state))?,
Insn::Defined { op_type, obj, pushval, v } => gen_defined(jit, asm, *op_type, *obj, *pushval, opnd!(v))?,
diff --git a/zjit/src/cruby.rs b/zjit/src/cruby.rs
index e5b66be850..82f0e39804 100644
--- a/zjit/src/cruby.rs
+++ b/zjit/src/cruby.rs
@@ -218,6 +218,9 @@ pub use rb_vm_get_special_object as vm_get_special_object;
/// Helper so we can get a Rust string for insn_name()
pub fn insn_name(opcode: usize) -> String {
+ if opcode >= VM_INSTRUCTION_SIZE.try_into().unwrap() {
+ return "<unknown>".into();
+ }
unsafe {
// Look up Ruby's NULL-terminated insn name string
let op_name = raw_insn_name(VALUE(opcode));
diff --git a/zjit/src/hir.rs b/zjit/src/hir.rs
index 30a6da201e..be320f6d74 100644
--- a/zjit/src/hir.rs
+++ b/zjit/src/hir.rs
@@ -393,6 +393,28 @@ impl PtrPrintMap {
}
}
+#[derive(Debug, Clone)]
+pub enum SideExitReason {
+ UnknownNewarraySend(vm_opt_newarray_send_type),
+ UnknownCallType,
+ UnknownOpcode(u32),
+}
+
+impl std::fmt::Display for SideExitReason {
+ fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
+ match self {
+ SideExitReason::UnknownOpcode(opcode) => write!(f, "UnknownOpcode({})", insn_name(*opcode as usize)),
+ SideExitReason::UnknownNewarraySend(VM_OPT_NEWARRAY_SEND_MAX) => write!(f, "UnknownNewarraySend(MAX)"),
+ SideExitReason::UnknownNewarraySend(VM_OPT_NEWARRAY_SEND_MIN) => write!(f, "UnknownNewarraySend(MIN)"),
+ SideExitReason::UnknownNewarraySend(VM_OPT_NEWARRAY_SEND_HASH) => write!(f, "UnknownNewarraySend(HASH)"),
+ SideExitReason::UnknownNewarraySend(VM_OPT_NEWARRAY_SEND_PACK) => write!(f, "UnknownNewarraySend(PACK)"),
+ SideExitReason::UnknownNewarraySend(VM_OPT_NEWARRAY_SEND_PACK_BUFFER) => write!(f, "UnknownNewarraySend(PACK_BUFFER)"),
+ SideExitReason::UnknownNewarraySend(VM_OPT_NEWARRAY_SEND_INCLUDE_P) => write!(f, "UnknownNewarraySend(INCLUDE_P)"),
+ _ => write!(f, "{self:?}"),
+ }
+ }
+}
+
/// An instruction in the SSA IR. The output of an instruction is referred to by the index of
/// the instruction ([`InsnId`]). SSA form enables this, and [`UnionFind`] ([`Function::find`])
/// helps with editing.
@@ -518,7 +540,7 @@ pub enum Insn {
PatchPoint(Invariant),
/// Side-exit into the interpreter.
- SideExit { state: InsnId },
+ SideExit { state: InsnId, reason: SideExitReason },
}
impl Insn {
@@ -714,7 +736,7 @@ impl<'a> std::fmt::Display for InsnPrinter<'a> {
Insn::ArrayPush { array, val, .. } => write!(f, "ArrayPush {array}, {val}"),
Insn::ObjToString { val, .. } => { write!(f, "ObjToString {val}") },
Insn::AnyToString { val, str, .. } => { write!(f, "AnyToString {val}, str: {str}") },
- Insn::SideExit { .. } => write!(f, "SideExit"),
+ Insn::SideExit { reason, .. } => write!(f, "SideExit {reason}"),
Insn::PutSpecialObject { value_type } => write!(f, "PutSpecialObject {value_type}"),
Insn::Throw { throw_state, val } => {
let mut state_string = match throw_state & VM_THROW_STATE_MASK {
@@ -1863,7 +1885,7 @@ impl Function {
worklist.push_back(state);
}
Insn::GetGlobal { state, .. } |
- Insn::SideExit { state } => worklist.push_back(state),
+ Insn::SideExit { state, .. } => worklist.push_back(state),
}
}
// Now remove all unnecessary instructions
@@ -2425,7 +2447,7 @@ pub fn iseq_to_hir(iseq: *const rb_iseq_t) -> Result<Function, ParseError> {
VM_OPT_NEWARRAY_SEND_MAX => (BOP_MAX, Insn::ArrayMax { elements, state: exit_id }),
_ => {
// Unknown opcode; side-exit into the interpreter
- fun.push_insn(block, Insn::SideExit { state: exit_id });
+ fun.push_insn(block, Insn::SideExit { state: exit_id, reason: SideExitReason::UnknownNewarraySend(method) });
break; // End the block
},
};
@@ -2651,7 +2673,7 @@ pub fn iseq_to_hir(iseq: *const rb_iseq_t) -> Result<Function, ParseError> {
if unknown_call_type(unsafe { rb_vm_ci_flag(call_info) }) {
// Unknown call type; side-exit into the interpreter
let exit_id = fun.push_insn(block, Insn::Snapshot { state: exit_state });
- fun.push_insn(block, Insn::SideExit { state: exit_id });
+ fun.push_insn(block, Insn::SideExit { state: exit_id, reason: SideExitReason::UnknownCallType });
break; // End the block
}
let argc = unsafe { vm_ci_argc((*cd).ci) };
@@ -2677,7 +2699,7 @@ pub fn iseq_to_hir(iseq: *const rb_iseq_t) -> Result<Function, ParseError> {
if unknown_call_type(unsafe { rb_vm_ci_flag(call_info) }) {
// Unknown call type; side-exit into the interpreter
let exit_id = fun.push_insn(block, Insn::Snapshot { state: exit_state });
- fun.push_insn(block, Insn::SideExit { state: exit_id });
+ fun.push_insn(block, Insn::SideExit { state: exit_id, reason: SideExitReason::UnknownCallType });
break; // End the block
}
let argc = unsafe { vm_ci_argc((*cd).ci) };
@@ -2708,7 +2730,7 @@ pub fn iseq_to_hir(iseq: *const rb_iseq_t) -> Result<Function, ParseError> {
if unknown_call_type(unsafe { rb_vm_ci_flag(call_info) }) {
// Unknown call type; side-exit into the interpreter
let exit_id = fun.push_insn(block, Insn::Snapshot { state: exit_state });
- fun.push_insn(block, Insn::SideExit { state: exit_id });
+ fun.push_insn(block, Insn::SideExit { state: exit_id, reason: SideExitReason::UnknownCallType });
break; // End the block
}
let argc = unsafe { vm_ci_argc((*cd).ci) };
@@ -2768,7 +2790,7 @@ pub fn iseq_to_hir(iseq: *const rb_iseq_t) -> Result<Function, ParseError> {
if unknown_call_type(unsafe { rb_vm_ci_flag(call_info) }) {
// Unknown call type; side-exit into the interpreter
let exit_id = fun.push_insn(block, Insn::Snapshot { state: exit_state });
- fun.push_insn(block, Insn::SideExit { state: exit_id });
+ fun.push_insn(block, Insn::SideExit { state: exit_id, reason: SideExitReason::UnknownCallType });
break; // End the block
}
let argc = unsafe { vm_ci_argc((*cd).ci) };
@@ -2796,7 +2818,7 @@ pub fn iseq_to_hir(iseq: *const rb_iseq_t) -> Result<Function, ParseError> {
if unknown_call_type(unsafe { rb_vm_ci_flag(call_info) }) {
// Unknown call type; side-exit into the interpreter
let exit_id = fun.push_insn(block, Insn::Snapshot { state: exit_state });
- fun.push_insn(block, Insn::SideExit { state: exit_id });
+ fun.push_insn(block, Insn::SideExit { state: exit_id, reason: SideExitReason::UnknownCallType });
break; // End the block
}
let argc = unsafe { vm_ci_argc((*cd).ci) };
@@ -2920,7 +2942,7 @@ pub fn iseq_to_hir(iseq: *const rb_iseq_t) -> Result<Function, ParseError> {
_ => {
// Unknown opcode; side-exit into the interpreter
let exit_id = fun.push_insn(block, Insn::Snapshot { state: exit_state });
- fun.push_insn(block, Insn::SideExit { state: exit_id });
+ fun.push_insn(block, Insn::SideExit { state: exit_id, reason: SideExitReason::UnknownOpcode(opcode) });
break; // End the block
}
}
@@ -3962,7 +3984,7 @@ mod tests {
fn test:
bb0(v0:BasicObject, v1:BasicObject):
v4:ArrayExact = ToArray v1
- SideExit
+ SideExit UnknownCallType
"#]]);
}
@@ -3974,7 +3996,7 @@ mod tests {
assert_method_hir("test", expect![[r#"
fn test:
bb0(v0:BasicObject, v1:BasicObject):
- SideExit
+ SideExit UnknownCallType
"#]]);
}
@@ -3987,7 +4009,7 @@ mod tests {
fn test:
bb0(v0:BasicObject, v1:BasicObject):
v3:Fixnum[1] = Const Value(1)
- SideExit
+ SideExit UnknownCallType
"#]]);
}
@@ -3999,7 +4021,7 @@ mod tests {
assert_method_hir("test", expect![[r#"
fn test:
bb0(v0:BasicObject, v1:BasicObject):
- SideExit
+ SideExit UnknownCallType
"#]]);
}
@@ -4013,7 +4035,7 @@ mod tests {
assert_method_hir("test", expect![[r#"
fn test:
bb0(v0:BasicObject):
- SideExit
+ SideExit UnknownOpcode(invokesuper)
"#]]);
}
@@ -4025,7 +4047,7 @@ mod tests {
assert_method_hir("test", expect![[r#"
fn test:
bb0(v0:BasicObject):
- SideExit
+ SideExit UnknownOpcode(invokesuper)
"#]]);
}
@@ -4037,7 +4059,7 @@ mod tests {
assert_method_hir("test", expect![[r#"
fn test:
bb0(v0:BasicObject, v1:BasicObject):
- SideExit
+ SideExit UnknownOpcode(invokesuperforward)
"#]]);
}
@@ -4058,7 +4080,7 @@ mod tests {
v9:StaticSymbol[:b] = Const Value(VALUE(0x1008))
v10:Fixnum[1] = Const Value(1)
v12:BasicObject = SendWithoutBlock v8, :core#hash_merge_ptr, v7, v9, v10
- SideExit
+ SideExit UnknownCallType
"#]]);
}
@@ -4073,7 +4095,7 @@ mod tests {
v4:ArrayExact = ToNewArray v1
v5:Fixnum[1] = Const Value(1)
ArrayPush v4, v5
- SideExit
+ SideExit UnknownCallType
"#]]);
}
@@ -4085,7 +4107,7 @@ mod tests {
assert_method_hir("test", expect![[r#"
fn test:
bb0(v0:BasicObject, v1:BasicObject):
- SideExit
+ SideExit UnknownOpcode(sendforward)
"#]]);
}
@@ -4154,7 +4176,7 @@ mod tests {
v3:NilClassExact = Const Value(nil)
v4:NilClassExact = Const Value(nil)
v7:BasicObject = SendWithoutBlock v1, :+, v2
- SideExit
+ SideExit UnknownNewarraySend(MIN)
"#]]);
}
@@ -4174,7 +4196,7 @@ mod tests {
v3:NilClassExact = Const Value(nil)
v4:NilClassExact = Const Value(nil)
v7:BasicObject = SendWithoutBlock v1, :+, v2
- SideExit
+ SideExit UnknownNewarraySend(HASH)
"#]]);
}
@@ -4196,7 +4218,7 @@ mod tests {
v7:BasicObject = SendWithoutBlock v1, :+, v2
v8:StringExact[VALUE(0x1000)] = Const Value(VALUE(0x1000))
v9:StringExact = StringCopy v8
- SideExit
+ SideExit UnknownNewarraySend(PACK)
"#]]);
}
@@ -4218,7 +4240,7 @@ mod tests {
v3:NilClassExact = Const Value(nil)
v4:NilClassExact = Const Value(nil)
v7:BasicObject = SendWithoutBlock v1, :+, v2
- SideExit
+ SideExit UnknownNewarraySend(INCLUDE_P)
"#]]);
}
@@ -4642,7 +4664,7 @@ mod tests {
v3:Fixnum[1] = Const Value(1)
v5:BasicObject = ObjToString v3
v7:String = AnyToString v3, str: v5
- SideExit
+ SideExit UnknownOpcode(concatstrings)
"#]]);
}
@@ -6220,7 +6242,7 @@ mod opt_tests {
v2:StringExact[VALUE(0x1000)] = Const Value(VALUE(0x1000))
v3:StringExact[VALUE(0x1008)] = Const Value(VALUE(0x1008))
v4:StringExact = StringCopy v3
- SideExit
+ SideExit UnknownOpcode(concatstrings)
"#]]);
}
@@ -6236,7 +6258,7 @@ mod opt_tests {
v3:Fixnum[1] = Const Value(1)
v10:BasicObject = SendWithoutBlock v3, :to_s
v7:String = AnyToString v3, str: v10
- SideExit
+ SideExit UnknownOpcode(concatstrings)
"#]]);
}