ZJIT: Specialize String#setbyte for fixnum case (#14927)

This commit is contained in:
Aiden Fox Ivey 2025-11-07 22:47:32 -05:00 committed by GitHub
parent c65f8b6370
commit 4aea392e69
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
Notes: git 2025-11-08 03:47:58 +00:00
Merged-By: k0kubun <takashikkbn@gmail.com>
5 changed files with 218 additions and 0 deletions

View File

@ -348,6 +348,7 @@ fn gen_insn(cb: &mut CodeBlock, jit: &mut JITState, asm: &mut Assembler, functio
let out_opnd = match insn {
&Insn::Const { val: Const::Value(val) } => gen_const_value(val),
&Insn::Const { val: Const::CPtr(val) } => gen_const_cptr(val),
&Insn::Const { val: Const::CInt64(val) } => gen_const_long(val),
Insn::Const { .. } => panic!("Unexpected Const in gen_insn: {insn}"),
Insn::NewArray { elements, state } => gen_new_array(asm, opnds!(elements), &function.frame_state(*state)),
Insn::NewHash { elements, state } => gen_new_hash(jit, asm, opnds!(elements), &function.frame_state(*state)),
@ -365,6 +366,7 @@ fn gen_insn(cb: &mut CodeBlock, jit: &mut JITState, asm: &mut Assembler, functio
Insn::StringConcat { strings, state, .. } if strings.is_empty() => return Err(*state),
Insn::StringConcat { strings, state } => gen_string_concat(jit, asm, opnds!(strings), &function.frame_state(*state)),
&Insn::StringGetbyteFixnum { string, index } => gen_string_getbyte_fixnum(asm, opnd!(string), opnd!(index)),
Insn::StringSetbyteFixnum { string, index, value } => gen_string_setbyte_fixnum(asm, opnd!(string), opnd!(index), opnd!(value)),
Insn::StringAppend { recv, other, state } => gen_string_append(jit, asm, opnd!(recv), opnd!(other), &function.frame_state(*state)),
Insn::StringIntern { val, state } => gen_intern(asm, opnd!(val), &function.frame_state(*state)),
Insn::ToRegexp { opt, values, state } => gen_toregexp(jit, asm, *opt, opnds!(values), &function.frame_state(*state)),
@ -407,12 +409,15 @@ fn gen_insn(cb: &mut CodeBlock, jit: &mut JITState, asm: &mut Assembler, functio
&Insn::IsBitNotEqual { left, right } => gen_is_bit_not_equal(asm, opnd!(left), opnd!(right)),
&Insn::BoxBool { val } => gen_box_bool(asm, opnd!(val)),
&Insn::BoxFixnum { val, state } => gen_box_fixnum(jit, asm, opnd!(val), &function.frame_state(state)),
&Insn::UnboxFixnum { val } => gen_unbox_fixnum(asm, opnd!(val)),
Insn::Test { val } => gen_test(asm, opnd!(val)),
Insn::GuardType { val, guard_type, state } => gen_guard_type(jit, asm, opnd!(val), *guard_type, &function.frame_state(*state)),
Insn::GuardTypeNot { val, guard_type, state } => gen_guard_type_not(jit, asm, opnd!(val), *guard_type, &function.frame_state(*state)),
Insn::GuardBitEquals { val, expected, state } => gen_guard_bit_equals(jit, asm, opnd!(val), *expected, &function.frame_state(*state)),
&Insn::GuardBlockParamProxy { level, state } => no_output!(gen_guard_block_param_proxy(jit, asm, level, &function.frame_state(state))),
Insn::GuardNotFrozen { val, state } => gen_guard_not_frozen(jit, asm, opnd!(val), &function.frame_state(*state)),
&Insn::GuardLess { left, right, state } => gen_guard_less(jit, asm, opnd!(left), opnd!(right), &function.frame_state(state)),
&Insn::GuardGreaterEq { left, right, state } => gen_guard_greater_eq(jit, asm, opnd!(left), opnd!(right), &function.frame_state(state)),
Insn::PatchPoint { invariant, state } => no_output!(gen_patch_point(jit, asm, invariant, &function.frame_state(*state))),
Insn::CCall { cfunc, args, name: _, return_type: _, elidable: _ } => gen_ccall(asm, *cfunc, opnds!(args)),
// Give up CCallWithFrame for 7+ args since asm.ccall() doesn't support it.
@ -571,6 +576,10 @@ fn gen_is_block_given(jit: &JITState, asm: &mut Assembler) -> Opnd {
}
}
fn gen_unbox_fixnum(asm: &mut Assembler, val: Opnd) -> Opnd {
asm.rshift(val, Opnd::UImm(1))
}
/// Get a local variable from a higher scope or the heap. `local_ep_offset` is in number of VALUEs.
/// We generate this instruction with level=0 only when the local variable is on the heap, so we
/// can't optimize the level=0 case using the SP register.
@ -642,6 +651,18 @@ fn gen_guard_not_frozen(jit: &JITState, asm: &mut Assembler, val: Opnd, state: &
val
}
fn gen_guard_less(jit: &JITState, asm: &mut Assembler, left: Opnd, right: Opnd, state: &FrameState) -> Opnd {
asm.cmp(left, right);
asm.jge(side_exit(jit, state, SideExitReason::GuardLess));
left
}
fn gen_guard_greater_eq(jit: &JITState, asm: &mut Assembler, left: Opnd, right: Opnd, state: &FrameState) -> Opnd {
asm.cmp(left, right);
asm.jl(side_exit(jit, state, SideExitReason::GuardGreaterEq));
left
}
fn gen_get_constant_path(jit: &JITState, asm: &mut Assembler, ic: *const iseq_inline_constant_cache, state: &FrameState) -> Opnd {
unsafe extern "C" {
fn rb_vm_opt_getconstant_path(ec: EcPtr, cfp: CfpPtr, ic: *const iseq_inline_constant_cache) -> VALUE;
@ -1047,6 +1068,10 @@ fn gen_const_cptr(val: *const u8) -> lir::Opnd {
Opnd::const_ptr(val)
}
fn gen_const_long(val: i64) -> lir::Opnd {
Opnd::Imm(val)
}
/// Compile a basic block argument
fn gen_param(asm: &mut Assembler, idx: usize) -> lir::Opnd {
// Allocate a register or a stack slot
@ -2302,6 +2327,11 @@ fn gen_string_getbyte_fixnum(asm: &mut Assembler, string: Opnd, index: Opnd) ->
asm_ccall!(asm, rb_str_getbyte, string, index)
}
fn gen_string_setbyte_fixnum(asm: &mut Assembler, string: Opnd, index: Opnd, value: Opnd) -> Opnd {
// rb_str_setbyte is not leaf, but we guard types and index ranges in HIR
asm_ccall!(asm, rb_str_setbyte, string, index, value)
}
fn gen_string_append(jit: &mut JITState, asm: &mut Assembler, string: Opnd, val: Opnd, state: &FrameState) -> Opnd {
gen_prepare_non_leaf_call(jit, asm, state);
asm_ccall!(asm, rb_str_buf_append, string, val)

View File

@ -199,6 +199,7 @@ pub fn init() -> Annotations {
annotate!(rb_cString, "size", types::Fixnum, no_gc, leaf, elidable);
annotate!(rb_cString, "length", types::Fixnum, no_gc, leaf, elidable);
annotate!(rb_cString, "getbyte", inline_string_getbyte);
annotate!(rb_cString, "setbyte", inline_string_setbyte);
annotate!(rb_cString, "empty?", types::BoolExact, no_gc, leaf, elidable);
annotate!(rb_cString, "<<", inline_string_append);
annotate!(rb_cString, "==", inline_string_eq);
@ -338,6 +339,31 @@ fn inline_string_getbyte(fun: &mut hir::Function, block: hir::BlockId, recv: hir
None
}
fn inline_string_setbyte(fun: &mut hir::Function, block: hir::BlockId, recv: hir::InsnId, args: &[hir::InsnId], state: hir::InsnId) -> Option<hir::InsnId> {
let &[index, value] = args else { return None; };
if fun.likely_a(index, types::Fixnum, state) && fun.likely_a(value, types::Fixnum, state) {
let index = fun.coerce_to(block, index, types::Fixnum, state);
let value = fun.coerce_to(block, value, types::Fixnum, state);
let unboxed_index = fun.push_insn(block, hir::Insn::UnboxFixnum { val: index });
let len = fun.push_insn(block, hir::Insn::LoadField {
recv,
id: ID!(len),
offset: RUBY_OFFSET_RSTRING_LEN as i32,
return_type: types::CInt64,
});
let unboxed_index = fun.push_insn(block, hir::Insn::GuardLess { left: unboxed_index, right: len, state });
let zero = fun.push_insn(block, hir::Insn::Const { val: hir::Const::CInt64(0) });
let _ = fun.push_insn(block, hir::Insn::GuardGreaterEq { left: unboxed_index, right: zero, state });
let recv = fun.push_insn(block, hir::Insn::GuardNotFrozen { val: recv, state });
let _ = fun.push_insn(block, hir::Insn::StringSetbyteFixnum { string: recv, index, value });
// String#setbyte returns the fixnum provided as its `value` argument back to the caller.
Some(value)
} else {
None
}
}
fn inline_string_append(fun: &mut hir::Function, block: hir::BlockId, recv: hir::InsnId, args: &[hir::InsnId], state: hir::InsnId) -> Option<hir::InsnId> {
let &[other] = args else { return None; };
// Inline only StringExact << String, which matches original type check from

View File

@ -471,6 +471,8 @@ pub enum SideExitReason {
GuardShape(ShapeId),
GuardBitEquals(Const),
GuardNotFrozen,
GuardLess,
GuardGreaterEq,
PatchPoint(Invariant),
CalleeSideExit,
ObjToStringFallback,
@ -600,6 +602,7 @@ pub enum Insn {
StringConcat { strings: Vec<InsnId>, state: InsnId },
/// Call rb_str_getbyte with known-Fixnum index
StringGetbyteFixnum { string: InsnId, index: InsnId },
StringSetbyteFixnum { string: InsnId, index: InsnId, value: InsnId },
StringAppend { recv: InsnId, other: InsnId, state: InsnId },
/// Combine count stack values into a regexp
@ -659,6 +662,7 @@ pub enum Insn {
BoxBool { val: InsnId },
/// Convert a C `long` to a Ruby `Fixnum`. Side exit on overflow.
BoxFixnum { val: InsnId, state: InsnId },
UnboxFixnum { val: InsnId },
// TODO(max): In iseq body types that are not ISEQ_TYPE_METHOD, rewrite to Constant false.
Defined { op_type: usize, obj: VALUE, pushval: VALUE, v: InsnId, state: InsnId },
GetConstantPath { ic: *const iseq_inline_constant_cache, state: InsnId },
@ -844,6 +848,10 @@ pub enum Insn {
GuardBlockParamProxy { level: u32, state: InsnId },
/// Side-exit if val is frozen.
GuardNotFrozen { val: InsnId, state: InsnId },
/// Side-exit if left is not greater than or equal to right (both operands are C long).
GuardGreaterEq { left: InsnId, right: InsnId, state: InsnId },
/// Side-exit if left is not less than right (both operands are C long).
GuardLess { left: InsnId, right: InsnId, state: InsnId },
/// Generate no code (or padding if necessary) and insert a patch point
/// that can be rewritten to a side exit when the Invariant is broken.
@ -1036,6 +1044,9 @@ impl<'a> std::fmt::Display for InsnPrinter<'a> {
Insn::StringGetbyteFixnum { string, index, .. } => {
write!(f, "StringGetbyteFixnum {string}, {index}")
}
Insn::StringSetbyteFixnum { string, index, value, .. } => {
write!(f, "StringSetbyteFixnum {string}, {index}, {value}")
}
Insn::StringAppend { recv, other, .. } => {
write!(f, "StringAppend {recv}, {other}")
}
@ -1068,6 +1079,7 @@ impl<'a> std::fmt::Display for InsnPrinter<'a> {
Insn::IsBitNotEqual { left, right } => write!(f, "IsBitNotEqual {left}, {right}"),
Insn::BoxBool { val } => write!(f, "BoxBool {val}"),
Insn::BoxFixnum { val, .. } => write!(f, "BoxFixnum {val}"),
Insn::UnboxFixnum { val } => write!(f, "UnboxFixnum {val}"),
Insn::Jump(target) => { write!(f, "Jump {target}") }
Insn::IfTrue { val, target } => { write!(f, "IfTrue {val}, {target}") }
Insn::IfFalse { val, target } => { write!(f, "IfFalse {val}, {target}") }
@ -1148,6 +1160,8 @@ impl<'a> std::fmt::Display for InsnPrinter<'a> {
&Insn::GuardShape { val, shape, .. } => { write!(f, "GuardShape {val}, {:p}", self.ptr_map.map_shape(shape)) },
Insn::GuardBlockParamProxy { level, .. } => write!(f, "GuardBlockParamProxy l{level}"),
Insn::GuardNotFrozen { val, .. } => write!(f, "GuardNotFrozen {val}"),
Insn::GuardLess { left, right, .. } => write!(f, "GuardLess {left}, {right}"),
Insn::GuardGreaterEq { left, right, .. } => write!(f, "GuardGreaterEq {left}, {right}"),
Insn::PatchPoint { invariant, .. } => { write!(f, "PatchPoint {}", invariant.print(self.ptr_map)) },
Insn::GetConstantPath { ic, .. } => { write!(f, "GetConstantPath {:p}", self.ptr_map.map_ptr(ic)) },
Insn::IsBlockGiven => { write!(f, "IsBlockGiven") },
@ -1702,6 +1716,7 @@ impl Function {
&StringIntern { val, state } => StringIntern { val: find!(val), state: find!(state) },
&StringConcat { ref strings, state } => StringConcat { strings: find_vec!(strings), state: find!(state) },
&StringGetbyteFixnum { string, index } => StringGetbyteFixnum { string: find!(string), index: find!(index) },
&StringSetbyteFixnum { string, index, value } => StringSetbyteFixnum { string: find!(string), index: find!(index), value: find!(value) },
&StringAppend { recv, other, state } => StringAppend { recv: find!(recv), other: find!(other), state: find!(state) },
&ToRegexp { opt, ref values, state } => ToRegexp { opt, values: find_vec!(values), state },
&Test { val } => Test { val: find!(val) },
@ -1711,6 +1726,7 @@ impl Function {
&IsBitNotEqual { left, right } => IsBitNotEqual { left: find!(left), right: find!(right) },
&BoxBool { val } => BoxBool { val: find!(val) },
&BoxFixnum { val, state } => BoxFixnum { val: find!(val), state: find!(state) },
&UnboxFixnum { val } => UnboxFixnum { val: find!(val) },
Jump(target) => Jump(find_branch_edge!(target)),
&IfTrue { val, ref target } => IfTrue { val: find!(val), target: find_branch_edge!(target) },
&IfFalse { val, ref target } => IfFalse { val: find!(val), target: find_branch_edge!(target) },
@ -1720,6 +1736,8 @@ impl Function {
&GuardShape { val, shape, state } => GuardShape { val: find!(val), shape, state },
&GuardBlockParamProxy { level, state } => GuardBlockParamProxy { level, state: find!(state) },
&GuardNotFrozen { val, state } => GuardNotFrozen { val: find!(val), state },
&GuardGreaterEq { left, right, state } => GuardGreaterEq { left: find!(left), right: find!(right), state },
&GuardLess { left, right, state } => GuardLess { left: find!(left), right: find!(right), state },
&FixnumAdd { left, right, state } => FixnumAdd { left: find!(left), right: find!(right), state },
&FixnumSub { left, right, state } => FixnumSub { left: find!(left), right: find!(right), state },
&FixnumMult { left, right, state } => FixnumMult { left: find!(left), right: find!(right), state },
@ -1906,10 +1924,12 @@ impl Function {
Insn::IsBitNotEqual { .. } => types::CBool,
Insn::BoxBool { .. } => types::BoolExact,
Insn::BoxFixnum { .. } => types::Fixnum,
Insn::UnboxFixnum { .. } => types::CInt64,
Insn::StringCopy { .. } => types::StringExact,
Insn::StringIntern { .. } => types::Symbol,
Insn::StringConcat { .. } => types::StringExact,
Insn::StringGetbyteFixnum { .. } => types::Fixnum.union(types::NilClass),
Insn::StringSetbyteFixnum { .. } => types::Fixnum,
Insn::StringAppend { .. } => types::StringExact,
Insn::ToRegexp { .. } => types::RegexpExact,
Insn::NewArray { .. } => types::ArrayExact,
@ -1932,6 +1952,8 @@ impl Function {
Insn::GuardBitEquals { val, expected, .. } => self.type_of(*val).intersection(Type::from_const(*expected)),
Insn::GuardShape { val, .. } => self.type_of(*val),
Insn::GuardNotFrozen { val, .. } => self.type_of(*val),
Insn::GuardLess { left, .. } => self.type_of(*left),
Insn::GuardGreaterEq { left, .. } => self.type_of(*left),
Insn::FixnumAdd { .. } => types::Fixnum,
Insn::FixnumSub { .. } => types::Fixnum,
Insn::FixnumMult { .. } => types::Fixnum,
@ -3284,6 +3306,11 @@ impl Function {
worklist.push_back(string);
worklist.push_back(index);
}
&Insn::StringSetbyteFixnum { string, index, value } => {
worklist.push_back(string);
worklist.push_back(index);
worklist.push_back(value);
}
&Insn::StringAppend { recv, other, state } => {
worklist.push_back(recv);
worklist.push_back(other);
@ -3316,6 +3343,16 @@ impl Function {
worklist.push_back(val);
worklist.push_back(state);
}
&Insn::GuardGreaterEq { left, right, state } => {
worklist.push_back(left);
worklist.push_back(right);
worklist.push_back(state);
}
&Insn::GuardLess { left, right, state } => {
worklist.push_back(left);
worklist.push_back(right);
worklist.push_back(state);
}
Insn::Snapshot { state } => {
worklist.extend(&state.stack);
worklist.extend(&state.locals);
@ -3430,6 +3467,7 @@ impl Function {
&Insn::GetSpecialNumber { state, .. } |
&Insn::ObjectAllocClass { state, .. } |
&Insn::SideExit { state, .. } => worklist.push_back(state),
&Insn::UnboxFixnum { val } => worklist.push_back(val),
}
}
@ -3792,6 +3830,7 @@ impl Function {
}
Insn::BoxBool { val } => self.assert_subtype(insn_id, val, types::CBool),
Insn::BoxFixnum { val, .. } => self.assert_subtype(insn_id, val, types::CInt64),
Insn::UnboxFixnum { val } => self.assert_subtype(insn_id, val, types::Fixnum),
Insn::SetGlobal { val, .. } => self.assert_subtype(insn_id, val, types::BasicObject),
Insn::GetIvar { self_val, .. } => self.assert_subtype(insn_id, self_val, types::BasicObject),
Insn::SetIvar { self_val, val, .. } => {
@ -3867,9 +3906,18 @@ impl Function {
}
Insn::GuardShape { val, .. } => self.assert_subtype(insn_id, val, types::BasicObject),
Insn::GuardNotFrozen { val, .. } => self.assert_subtype(insn_id, val, types::BasicObject),
Insn::GuardLess { left, right, .. } | Insn::GuardGreaterEq { left, right, .. } => {
self.assert_subtype(insn_id, left, types::CInt64)?;
self.assert_subtype(insn_id, right, types::CInt64)
},
Insn::StringGetbyteFixnum { string, index } => {
self.assert_subtype(insn_id, string, types::String)?;
self.assert_subtype(insn_id, index, types::Fixnum)
},
Insn::StringSetbyteFixnum { string, index, value } => {
self.assert_subtype(insn_id, string, types::String)?;
self.assert_subtype(insn_id, index, types::Fixnum)?;
self.assert_subtype(insn_id, value, types::Fixnum)
}
_ => Ok(()),
}

View File

@ -5801,6 +5801,116 @@ mod hir_opt_tests {
");
}
#[test]
fn test_optimize_string_setbyte_fixnum() {
eval(r#"
def test(s, idx, val)
s.setbyte(idx, val)
end
test("foo", 0, 127)
"#);
assert_snapshot!(hir_string("test"), @r"
fn test@<compiled>:3:
bb0():
EntryPoint interpreter
v1:BasicObject = LoadSelf
v2:BasicObject = GetLocal l0, SP@6
v3:BasicObject = GetLocal l0, SP@5
v4:BasicObject = GetLocal l0, SP@4
Jump bb2(v1, v2, v3, v4)
bb1(v7:BasicObject, v8:BasicObject, v9:BasicObject, v10:BasicObject):
EntryPoint JIT(0)
Jump bb2(v7, v8, v9, v10)
bb2(v12:BasicObject, v13:BasicObject, v14:BasicObject, v15:BasicObject):
PatchPoint MethodRedefined(String@0x1000, setbyte@0x1008, cme:0x1010)
PatchPoint NoSingletonClass(String@0x1000)
v29:StringExact = GuardType v13, StringExact
v30:Fixnum = GuardType v14, Fixnum
v31:Fixnum = GuardType v15, Fixnum
v32:CInt64 = UnboxFixnum v30
v33:CInt64 = LoadField v29, :len@0x1038
v34:CInt64 = GuardLess v32, v33
v35:CInt64[0] = Const CInt64(0)
v36:CInt64 = GuardGreaterEq v34, v35
v37:StringExact = GuardNotFrozen v29
v38:Fixnum = StringSetbyteFixnum v37, v30, v31
IncrCounter inline_cfunc_optimized_send_count
CheckInterrupts
Return v31
");
}
#[test]
fn test_optimize_string_subclass_setbyte_fixnum() {
eval(r#"
class MyString < String
end
def test(s, idx, val)
s.setbyte(idx, val)
end
test(MyString.new('foo'), 0, 127)
"#);
assert_snapshot!(hir_string("test"), @r"
fn test@<compiled>:5:
bb0():
EntryPoint interpreter
v1:BasicObject = LoadSelf
v2:BasicObject = GetLocal l0, SP@6
v3:BasicObject = GetLocal l0, SP@5
v4:BasicObject = GetLocal l0, SP@4
Jump bb2(v1, v2, v3, v4)
bb1(v7:BasicObject, v8:BasicObject, v9:BasicObject, v10:BasicObject):
EntryPoint JIT(0)
Jump bb2(v7, v8, v9, v10)
bb2(v12:BasicObject, v13:BasicObject, v14:BasicObject, v15:BasicObject):
PatchPoint MethodRedefined(MyString@0x1000, setbyte@0x1008, cme:0x1010)
PatchPoint NoSingletonClass(MyString@0x1000)
v29:StringSubclass[class_exact:MyString] = GuardType v13, StringSubclass[class_exact:MyString]
v30:Fixnum = GuardType v14, Fixnum
v31:Fixnum = GuardType v15, Fixnum
v32:CInt64 = UnboxFixnum v30
v33:CInt64 = LoadField v29, :len@0x1038
v34:CInt64 = GuardLess v32, v33
v35:CInt64[0] = Const CInt64(0)
v36:CInt64 = GuardGreaterEq v34, v35
v37:StringSubclass[class_exact:MyString] = GuardNotFrozen v29
v38:Fixnum = StringSetbyteFixnum v37, v30, v31
IncrCounter inline_cfunc_optimized_send_count
CheckInterrupts
Return v31
");
}
#[test]
fn test_do_not_optimize_string_setbyte_non_fixnum() {
eval(r#"
def test(s, idx, val)
s.setbyte(idx, val)
end
test("foo", 0, 3.14)
"#);
assert_snapshot!(hir_string("test"), @r"
fn test@<compiled>:3:
bb0():
EntryPoint interpreter
v1:BasicObject = LoadSelf
v2:BasicObject = GetLocal l0, SP@6
v3:BasicObject = GetLocal l0, SP@5
v4:BasicObject = GetLocal l0, SP@4
Jump bb2(v1, v2, v3, v4)
bb1(v7:BasicObject, v8:BasicObject, v9:BasicObject, v10:BasicObject):
EntryPoint JIT(0)
Jump bb2(v7, v8, v9, v10)
bb2(v12:BasicObject, v13:BasicObject, v14:BasicObject, v15:BasicObject):
PatchPoint MethodRedefined(String@0x1000, setbyte@0x1008, cme:0x1010)
PatchPoint NoSingletonClass(String@0x1000)
v29:StringExact = GuardType v13, StringExact
v30:BasicObject = CCallWithFrame setbyte@0x1038, v29, v14, v15
CheckInterrupts
Return v30
");
}
#[test]
fn test_specialize_string_empty() {
eval(r#"

View File

@ -146,6 +146,8 @@ make_counters! {
exit_guard_int_equals_failure,
exit_guard_shape_failure,
exit_guard_not_frozen_failure,
exit_guard_less_failure,
exit_guard_greater_eq_failure,
exit_patchpoint_bop_redefined,
exit_patchpoint_method_redefined,
exit_patchpoint_stable_constant_names,
@ -390,6 +392,8 @@ pub fn side_exit_counter(reason: crate::hir::SideExitReason) -> Counter {
GuardBitEquals(_) => exit_guard_bit_equals_failure,
GuardShape(_) => exit_guard_shape_failure,
GuardNotFrozen => exit_guard_not_frozen_failure,
GuardLess => exit_guard_less_failure,
GuardGreaterEq => exit_guard_greater_eq_failure,
CalleeSideExit => exit_callee_side_exit,
ObjToStringFallback => exit_obj_to_string_fallback,
Interrupt => exit_interrupt,