From 945eab1d12637e97c984ae535c10e6f451664bcf Mon Sep 17 00:00:00 2001 From: Max Bernstein Date: Thu, 21 May 2026 07:55:14 -0700 Subject: [PATCH 01/11] ZJIT: Print module name in HIR Type display (#17039) Go from `ModuleExact[VALUE(0x1008)]` to `ModuleExact[Enumerable@0x1008]` if the module's name isn't `nil`. --- zjit/src/cruby.rs | 13 ++++++++++ zjit/src/hir/opt_tests.rs | 51 ++++++++++++++++++++++++++++++++------- zjit/src/hir_type/mod.rs | 9 +++++++ 3 files changed, 64 insertions(+), 9 deletions(-) diff --git a/zjit/src/cruby.rs b/zjit/src/cruby.rs index 6a5dd234bb56e3..14db7c57d75718 100644 --- a/zjit/src/cruby.rs +++ b/zjit/src/cruby.rs @@ -1471,6 +1471,19 @@ pub fn get_class_name(class: VALUE) -> String { name } +// Return the module name for a given module or class. For anonymous modules, returns None since +// rb_mod_name returns Qnil. +pub fn get_module_name(module: VALUE) -> Option { + // type checks for rb_mod_name() + assert!(unsafe { RB_TYPE_P(module, RUBY_T_MODULE) || RB_TYPE_P(module, RUBY_T_CLASS) }, "Expected class or module"); + let name = unsafe { rb_mod_name(module) }; + if name == Qnil { + None + } else { + Some(ruby_str_to_rust_string(name)) + } +} + #[cfg(test)] mod class_name_tests { diff --git a/zjit/src/hir/opt_tests.rs b/zjit/src/hir/opt_tests.rs index 3435347d338c21..50d8979add5f50 100644 --- a/zjit/src/hir/opt_tests.rs +++ b/zjit/src/hir/opt_tests.rs @@ -3207,7 +3207,7 @@ mod hir_opt_tests { bb3(v8:BasicObject, v9:NilClass): PatchPoint SingleRactorMode PatchPoint StableConstantNames(0x1000, M) - v29:ModuleExact[VALUE(0x1008)] = Const Value(VALUE(0x1008)) + v29:ModuleExact[M@0x1008] = Const Value(VALUE(0x1008)) PatchPoint NoSingletonClass(Module@0x1010) PatchPoint MethodRedefined(Module@0x1010, name@0x1018, cme:0x1020) v33:StringExact|NilClass = CCall v29, :Module#name@0x1048 @@ -3323,9 +3323,9 @@ mod hir_opt_tests { bb3(v6:BasicObject): PatchPoint SingleRactorMode PatchPoint StableConstantNames(0x1000, Enumerable) - v22:ModuleExact[VALUE(0x1008)] = Const Value(VALUE(0x1008)) + v22:ModuleExact[Enumerable@0x1008] = Const Value(VALUE(0x1008)) PatchPoint StableConstantNames(0x1010, Kernel) - v25:ModuleExact[VALUE(0x1018)] = Const Value(VALUE(0x1018)) + v25:ModuleExact[Kernel@0x1018] = Const Value(VALUE(0x1018)) v14:ArrayExact = NewArray v22, v25 CheckInterrupts Return v14 @@ -3353,7 +3353,7 @@ mod hir_opt_tests { bb3(v6:BasicObject): PatchPoint SingleRactorMode PatchPoint StableConstantNames(0x1000, MY_MODULE) - v18:ModuleSubclass[VALUE(0x1008)] = Const Value(VALUE(0x1008)) + v18:ModuleSubclass[MY_MODULE@0x1008] = Const Value(VALUE(0x1008)) CheckInterrupts Return v18 "); @@ -3575,7 +3575,7 @@ mod hir_opt_tests { bb3(v6:BasicObject): PatchPoint SingleRactorMode PatchPoint StableConstantNames(0x1000, M) - v20:ModuleExact[VALUE(0x1008)] = Const Value(VALUE(0x1008)) + v20:ModuleExact[M@0x1008] = Const Value(VALUE(0x1008)) PatchPoint NoSingletonClass(Module@0x1010) PatchPoint MethodRedefined(Module@0x1010, class@0x1018, cme:0x1020) v25:Class[Module@0x1010] = Const Value(VALUE(0x1010)) @@ -4392,7 +4392,7 @@ mod hir_opt_tests { bb3(v6:BasicObject): PatchPoint SingleRactorMode PatchPoint StableConstantNames(0x1000, Kernel) - v18:ModuleExact[VALUE(0x1008)] = Const Value(VALUE(0x1008)) + v18:ModuleExact[Kernel@0x1008] = Const Value(VALUE(0x1008)) CheckInterrupts Return v18 "); @@ -13010,7 +13010,7 @@ mod hir_opt_tests { bb3(v9:BasicObject, v10:BasicObject): PatchPoint SingleRactorMode PatchPoint StableConstantNames(0x1008, Kernel) - v27:ModuleExact[VALUE(0x1010)] = Const Value(VALUE(0x1010)) + v27:ModuleExact[Kernel@0x1010] = Const Value(VALUE(0x1010)) PatchPoint NoEPEscape(test) PatchPoint MethodRedefined(Module@0x1018, ===@0x1020, cme:0x1028) v30:BoolExact = CCall v27, :Module#===@0x1050, v10 @@ -13073,7 +13073,7 @@ mod hir_opt_tests { bb3(v9:BasicObject, v10:BasicObject): PatchPoint SingleRactorMode PatchPoint StableConstantNames(0x1008, Kernel) - v25:ModuleExact[VALUE(0x1010)] = Const Value(VALUE(0x1010)) + v25:ModuleExact[Kernel@0x1010] = Const Value(VALUE(0x1010)) PatchPoint NoSingletonClass(String@0x1018) PatchPoint MethodRedefined(String@0x1018, is_a?@0x1020, cme:0x1028) v29:StringExact = GuardType v10, StringExact @@ -13206,7 +13206,7 @@ mod hir_opt_tests { bb3(v9:BasicObject, v10:BasicObject): PatchPoint SingleRactorMode PatchPoint StableConstantNames(0x1008, Kernel) - v25:ModuleExact[VALUE(0x1010)] = Const Value(VALUE(0x1010)) + v25:ModuleExact[Kernel@0x1010] = Const Value(VALUE(0x1010)) PatchPoint NoSingletonClass(String@0x1018) PatchPoint MethodRedefined(String@0x1018, kind_of?@0x1020, cme:0x1028) v29:StringExact = GuardType v10, StringExact @@ -13640,6 +13640,39 @@ mod hir_opt_tests { "); } + #[test] + fn test_print_nil_module_name() { + eval(r#" + X = [Module.new].freeze + def test = X[0] + test + "#); + assert_snapshot!(hir_string("test"), @" + fn test@:3: + bb1(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + Jump bb3(v1) + bb2(): + EntryPoint JIT(0) + v4:BasicObject = LoadArg :self@0 + Jump bb3(v4) + bb3(v6:BasicObject): + PatchPoint SingleRactorMode + PatchPoint StableConstantNames(0x1000, X) + v23:ArrayExact[VALUE(0x1008)] = Const Value(VALUE(0x1008)) + v12:Fixnum[0] = Const Value(0) + PatchPoint NoSingletonClass(Array@0x1010) + PatchPoint MethodRedefined(Array@0x1010, []@0x1018, cme:0x1020) + v34:CInt64[0] = Const CInt64(0) + v28:CInt64 = ArrayLength v23 + v29:CInt64[0] = GuardLess v34, v28 + v35:ModuleExact[VALUE(0x1048)] = Const Value(VALUE(0x1048)) + CheckInterrupts + Return v35 + "); + } + #[test] fn no_load_from_ep_right_after_entrypoint() { let formatted = eval(" diff --git a/zjit/src/hir_type/mod.rs b/zjit/src/hir_type/mod.rs index 206b74e7d38de3..12e7986a397ae3 100644 --- a/zjit/src/hir_type/mod.rs +++ b/zjit/src/hir_type/mod.rs @@ -6,6 +6,7 @@ use crate::cruby::{rb_block_param_proxy, Qfalse, Qnil, Qtrue, RUBY_T_ARRAY, RUBY use crate::cruby::{rb_cInteger, rb_cFloat, rb_cArray, rb_cHash, rb_cString, rb_cSymbol, rb_cRange, rb_cModule, rb_zjit_singleton_class_p}; use crate::cruby::ClassRelationship; use crate::cruby::get_class_name; +use crate::cruby::get_module_name; use crate::cruby::ruby_sym_to_rust_string; use crate::cruby::rb_mRubyVMFrozenCore; use crate::cruby::rb_obj_class; @@ -80,6 +81,14 @@ fn write_spec(f: &mut std::fmt::Formatter, printer: &TypePrinter) -> std::fmt::R Specialization::Object(val) if ty.is_subtype(types::Symbol) => write!(f, "[:{}]", ruby_sym_to_rust_string(val)), Specialization::Object(val) if ty.is_subtype(types::Class) => write!(f, "[{}@{:p}]", get_class_name(val), printer.ptr_map.map_ptr(val.0 as *const std::ffi::c_void)), + Specialization::Object(val) if ty.is_subtype(types::Module) => { + if let Some(name) = get_module_name(val) { + write!(f, "[{}@{:p}]", name, printer.ptr_map.map_ptr(val.0 as *const std::ffi::c_void)) + } else { + // Same as generic Specialization::Object + write!(f, "[{}]", val.print(printer.ptr_map)) + } + } Specialization::Object(val) => write!(f, "[{}]", val.print(printer.ptr_map)), // TODO(max): Ensure singleton classes never have Type specialization Specialization::Type(val) if unsafe { rb_zjit_singleton_class_p(val) } => From cf2170943b2e84d1e5c656c762b172fe573aa73a Mon Sep 17 00:00:00 2001 From: Max Bernstein Date: Tue, 19 May 2026 17:31:50 -0400 Subject: [PATCH 02/11] ZJIT: Fold ArrayLength on frozen arrays --- zjit/src/hir.rs | 9 ++++ zjit/src/hir/opt_tests.rs | 94 ++++++++++++++++++++++++++++++++------- 2 files changed, 86 insertions(+), 17 deletions(-) diff --git a/zjit/src/hir.rs b/zjit/src/hir.rs index fdc82c9552e41f..9087fdb3438470 100644 --- a/zjit/src/hir.rs +++ b/zjit/src/hir.rs @@ -5088,6 +5088,15 @@ impl Function { _ => insn_id, } } + Insn::ArrayLength { array } => { + match self.type_of(array).ruby_object() { + Some(array_obj) if array_obj.is_frozen() => { + let length = unsafe { rb_jit_array_len(array_obj) }; + self.new_insn(Insn::Const { val: Const::CInt64(length) }) + } + _ => insn_id, + } + } Insn::UnboxFixnum { val } => { let recv_type = self.type_of(val); match recv_type.fixnum_value() { diff --git a/zjit/src/hir/opt_tests.rs b/zjit/src/hir/opt_tests.rs index 50d8979add5f50..97dea46e586d4c 100644 --- a/zjit/src/hir/opt_tests.rs +++ b/zjit/src/hir/opt_tests.rs @@ -1076,9 +1076,9 @@ mod hir_opt_tests { PatchPoint NoSingletonClass(Array@0x1008) PatchPoint MethodRedefined(Array@0x1008, []@0x1010, cme:0x1018) v32:CInt64[-10] = Const CInt64(-10) - v26:CInt64 = ArrayLength v11 - v27:CInt64[-10] = GuardLess v32, v26 - v28:CInt64 = AdjustBounds v27, v26 + v33:CInt64[3] = Const CInt64(3) + v27:CInt64[-10] = GuardLess v32, v33 + v28:CInt64 = AdjustBounds v27, v33 v29:CInt64[0] = Const CInt64(0) v30:CInt64 = GuardGreaterEq v28, v29 v31:BasicObject = ArrayAref v11, v30 @@ -6492,11 +6492,11 @@ mod hir_opt_tests { PatchPoint NoSingletonClass(Array@0x1008) PatchPoint MethodRedefined(Array@0x1008, []@0x1010, cme:0x1018) v32:CInt64[1] = Const CInt64(1) - v26:CInt64 = ArrayLength v11 - v27:CInt64[1] = GuardLess v32, v26 - v33:Fixnum[5] = Const Value(5) + v33:CInt64[3] = Const CInt64(3) + v27:CInt64[1] = GuardLess v32, v33 + v34:Fixnum[5] = Const Value(5) CheckInterrupts - Return v33 + Return v34 "); } @@ -6522,9 +6522,9 @@ mod hir_opt_tests { PatchPoint NoSingletonClass(Array@0x1008) PatchPoint MethodRedefined(Array@0x1008, []@0x1010, cme:0x1018) v32:CInt64[-3] = Const CInt64(-3) - v26:CInt64 = ArrayLength v11 - v27:CInt64[-3] = GuardLess v32, v26 - v28:CInt64 = AdjustBounds v27, v26 + v33:CInt64[3] = Const CInt64(3) + v27:CInt64[-3] = GuardLess v32, v33 + v28:CInt64 = AdjustBounds v27, v33 v29:CInt64[0] = Const CInt64(0) v30:CInt64 = GuardGreaterEq v28, v29 v31:BasicObject = ArrayAref v11, v30 @@ -6555,9 +6555,9 @@ mod hir_opt_tests { PatchPoint NoSingletonClass(Array@0x1008) PatchPoint MethodRedefined(Array@0x1008, []@0x1010, cme:0x1018) v32:CInt64[-10] = Const CInt64(-10) - v26:CInt64 = ArrayLength v11 - v27:CInt64[-10] = GuardLess v32, v26 - v28:CInt64 = AdjustBounds v27, v26 + v33:CInt64[3] = Const CInt64(3) + v27:CInt64[-10] = GuardLess v32, v33 + v28:CInt64 = AdjustBounds v27, v33 v29:CInt64[0] = Const CInt64(0) v30:CInt64 = GuardGreaterEq v28, v29 v31:BasicObject = ArrayAref v11, v30 @@ -6588,11 +6588,11 @@ mod hir_opt_tests { PatchPoint NoSingletonClass(Array@0x1008) PatchPoint MethodRedefined(Array@0x1008, []@0x1010, cme:0x1018) v32:CInt64[10] = Const CInt64(10) - v26:CInt64 = ArrayLength v11 - v27:CInt64[10] = GuardLess v32, v26 - v33:NilClass = Const Value(nil) + v33:CInt64[3] = Const CInt64(3) + v27:CInt64[10] = GuardLess v32, v33 + v34:NilClass = Const Value(nil) CheckInterrupts - Return v33 + Return v34 "); } @@ -16558,4 +16558,64 @@ mod hir_opt_tests { Return v215 "); } + + #[test] + fn test_dont_fold_array_length() { + eval(r#" + A = [1, 2, 3, 4] + def test = A.length + test + "#); + assert_snapshot!(hir_string("test"), @" + fn test@:3: + bb1(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + Jump bb3(v1) + bb2(): + EntryPoint JIT(0) + v4:BasicObject = LoadArg :self@0 + Jump bb3(v4) + bb3(v6:BasicObject): + PatchPoint SingleRactorMode + PatchPoint StableConstantNames(0x1000, A) + v21:ArrayExact[VALUE(0x1008)] = Const Value(VALUE(0x1008)) + PatchPoint NoSingletonClass(Array@0x1010) + PatchPoint MethodRedefined(Array@0x1010, length@0x1018, cme:0x1020) + v25:CInt64 = ArrayLength v21 + v26:Fixnum = BoxFixnum v25 + CheckInterrupts + Return v26 + "); + } + + #[test] + fn test_fold_frozen_array_length() { + eval(r#" + A = [1, 2, 3, 4].freeze + def test = A.length + test + "#); + assert_snapshot!(hir_string("test"), @" + fn test@:3: + bb1(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + Jump bb3(v1) + bb2(): + EntryPoint JIT(0) + v4:BasicObject = LoadArg :self@0 + Jump bb3(v4) + bb3(v6:BasicObject): + PatchPoint SingleRactorMode + PatchPoint StableConstantNames(0x1000, A) + v21:ArrayExact[VALUE(0x1008)] = Const Value(VALUE(0x1008)) + PatchPoint NoSingletonClass(Array@0x1010) + PatchPoint MethodRedefined(Array@0x1010, length@0x1018, cme:0x1020) + v27:CInt64[4] = Const CInt64(4) + v26:Fixnum = BoxFixnum v27 + CheckInterrupts + Return v26 + "); + } } From 59d376b1baee1991ffd857e38465146911cc8836 Mon Sep 17 00:00:00 2001 From: Max Bernstein Date: Tue, 19 May 2026 17:37:10 -0400 Subject: [PATCH 03/11] ZJIT: Fold GuardLess Also plumb through a `SideExitReason` because the falsy fold to `SideExit` requires one. --- zjit/src/codegen.rs | 6 +++--- zjit/src/cruby_methods.rs | 8 ++++---- zjit/src/hir.rs | 16 ++++++++++++++-- zjit/src/hir/opt_tests.rs | 26 ++++++-------------------- 4 files changed, 27 insertions(+), 29 deletions(-) diff --git a/zjit/src/codegen.rs b/zjit/src/codegen.rs index f4d62fc7bcef43..7d7aca1c83d44c 100644 --- a/zjit/src/codegen.rs +++ b/zjit/src/codegen.rs @@ -679,7 +679,7 @@ fn gen_insn(cb: &mut CodeBlock, jit: &mut JITState, asm: &mut Assembler, functio &Insn::GuardBitEquals { val, expected, reason, state, recompile } => gen_guard_bit_equals(jit, asm, opnd!(val), expected, reason, recompile, &function.frame_state(state)), &Insn::GuardAnyBitSet { val, mask, reason, state, .. } => gen_guard_any_bit_set(jit, asm, opnd!(val), mask, reason, &function.frame_state(state)), &Insn::GuardNoBitsSet { val, mask, reason, state, .. } => gen_guard_no_bits_set(jit, asm, opnd!(val), mask, reason, &function.frame_state(state)), - &Insn::GuardLess { left, right, state } => gen_guard_less(jit, asm, opnd!(left), opnd!(right), &function.frame_state(state)), + &Insn::GuardLess { left, right, reason, state } => gen_guard_less(jit, asm, opnd!(left), opnd!(right), reason, &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, recv, args, name, owner: _, return_type: _, elidable: _ } => gen_ccall(asm, *cfunc, *name, opnd!(recv), opnds!(args)), @@ -885,9 +885,9 @@ fn gen_getblockparam(jit: &mut JITState, asm: &mut Assembler, ep_offset: u32, le asm.load(Opnd::mem(VALUE_BITS, ep, offset)) } -fn gen_guard_less(jit: &mut JITState, asm: &mut Assembler, left: Opnd, right: Opnd, state: &FrameState) -> Opnd { +fn gen_guard_less(jit: &mut JITState, asm: &mut Assembler, left: Opnd, right: Opnd, reason: SideExitReason, state: &FrameState) -> Opnd { asm.cmp(left, right); - asm.jge(jit, side_exit(jit, state, SideExitReason::GuardLess)); + asm.jge(jit, side_exit(jit, state, reason)); left } diff --git a/zjit/src/cruby_methods.rs b/zjit/src/cruby_methods.rs index db0993072166ca..05b00550328f11 100644 --- a/zjit/src/cruby_methods.rs +++ b/zjit/src/cruby_methods.rs @@ -367,7 +367,7 @@ fn inline_array_aref(fun: &mut hir::Function, block: hir::BlockId, recv: hir::In let index = fun.coerce_to(block, index, types::Fixnum, state); let index = fun.push_insn(block, hir::Insn::UnboxFixnum { val: index }); let length = fun.push_insn(block, hir::Insn::ArrayLength { array: recv }); - let index = fun.push_insn(block, hir::Insn::GuardLess { left: index, right: length, state }); + let index = fun.push_insn(block, hir::Insn::GuardLess { left: index, right: length, reason: SideExitReason::GuardLess, state }); let index = fun.push_insn(block, hir::Insn::AdjustBounds { index, length }); let zero = fun.push_insn(block, hir::Insn::Const { val: hir::Const::CInt64(0) }); use crate::hir::SideExitReason; @@ -392,7 +392,7 @@ fn inline_array_aset(fun: &mut hir::Function, block: hir::BlockId, recv: hir::In // Bounds check: unbox Fixnum index and guard 0 <= idx < length. let index = fun.push_insn(block, hir::Insn::UnboxFixnum { val: index }); let length = fun.push_insn(block, hir::Insn::ArrayLength { array: recv }); - let index = fun.push_insn(block, hir::Insn::GuardLess { left: index, right: length, state }); + let index = fun.push_insn(block, hir::Insn::GuardLess { left: index, right: length, reason: SideExitReason::GuardLess, state }); let index = fun.push_insn(block, hir::Insn::AdjustBounds { index, length }); let zero = fun.push_insn(block, hir::Insn::Const { val: hir::Const::CInt64(0) }); use crate::hir::SideExitReason; @@ -492,7 +492,7 @@ fn inline_string_getbyte(fun: &mut hir::Function, block: hir::BlockId, recv: hir // the data dependency is gone (say, the StringGetbyte is elided), they can also be elided. // // This is unlike most other guards. - let unboxed_index = fun.push_insn(block, hir::Insn::GuardLess { left: unboxed_index, right: len, state }); + let unboxed_index = fun.push_insn(block, hir::Insn::GuardLess { left: unboxed_index, right: len, reason: SideExitReason::GuardLess, state }); let unboxed_index = fun.push_insn(block, hir::Insn::AdjustBounds { index: unboxed_index, length: len }); let zero = fun.push_insn(block, hir::Insn::Const { val: hir::Const::CInt64(0) }); use crate::hir::SideExitReason; @@ -516,7 +516,7 @@ fn inline_string_setbyte(fun: &mut hir::Function, block: hir::BlockId, recv: hir 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 unboxed_index = fun.push_insn(block, hir::Insn::GuardLess { left: unboxed_index, right: len, reason: SideExitReason::GuardLess, state }); let unboxed_index = fun.push_insn(block, hir::Insn::AdjustBounds { index: unboxed_index, length: len }); let zero = fun.push_insn(block, hir::Insn::Const { val: hir::Const::CInt64(0) }); use crate::hir::SideExitReason; diff --git a/zjit/src/hir.rs b/zjit/src/hir.rs index 9087fdb3438470..a62e776d2fb70d 100644 --- a/zjit/src/hir.rs +++ b/zjit/src/hir.rs @@ -1157,7 +1157,7 @@ pub enum Insn { /// Side-exit if left is not greater than or equal to right (both operands are C long). GuardGreaterEq { left: InsnId, right: InsnId, reason: SideExitReason, state: InsnId }, /// Side-exit if left is not less than right (both operands are C long). - GuardLess { left: InsnId, right: InsnId, state: InsnId }, + GuardLess { left: InsnId, right: InsnId, reason: SideExitReason, 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. @@ -1311,7 +1311,7 @@ macro_rules! for_each_operand_impl { $visit_one!(state); } Insn::GuardGreaterEq { left, right, state, .. } - | Insn::GuardLess { left, right, state } => { + | Insn::GuardLess { left, right, state, .. } => { $visit_one!(left); $visit_one!(right); $visit_one!(state); @@ -5116,6 +5116,18 @@ impl Function { _ => insn_id, } }, + Insn::GuardLess { left, right, state, reason } => { + let left_num = self.type_of(left).cint64_value(); + let right_num = self.type_of(right).cint64_value(); + match (left_num, right_num) { + (Some(l), Some(r)) if l < r => { + self.make_equal_to(insn_id, left); + continue + }, + (Some(_), Some(_)) => self.new_insn(Insn::SideExit { state, reason, recompile: None }), + _ => insn_id, + } + }, Insn::GuardBitEquals { val, expected, .. } => { let recv_type = self.type_of(val); if recv_type.has_value(expected) { diff --git a/zjit/src/hir/opt_tests.rs b/zjit/src/hir/opt_tests.rs index 97dea46e586d4c..172cb92b7b9596 100644 --- a/zjit/src/hir/opt_tests.rs +++ b/zjit/src/hir/opt_tests.rs @@ -1077,8 +1077,7 @@ mod hir_opt_tests { PatchPoint MethodRedefined(Array@0x1008, []@0x1010, cme:0x1018) v32:CInt64[-10] = Const CInt64(-10) v33:CInt64[3] = Const CInt64(3) - v27:CInt64[-10] = GuardLess v32, v33 - v28:CInt64 = AdjustBounds v27, v33 + v28:CInt64 = AdjustBounds v32, v33 v29:CInt64[0] = Const CInt64(0) v30:CInt64 = GuardGreaterEq v28, v29 v31:BasicObject = ArrayAref v11, v30 @@ -6491,9 +6490,6 @@ mod hir_opt_tests { v13:Fixnum[1] = Const Value(1) PatchPoint NoSingletonClass(Array@0x1008) PatchPoint MethodRedefined(Array@0x1008, []@0x1010, cme:0x1018) - v32:CInt64[1] = Const CInt64(1) - v33:CInt64[3] = Const CInt64(3) - v27:CInt64[1] = GuardLess v32, v33 v34:Fixnum[5] = Const Value(5) CheckInterrupts Return v34 @@ -6523,8 +6519,7 @@ mod hir_opt_tests { PatchPoint MethodRedefined(Array@0x1008, []@0x1010, cme:0x1018) v32:CInt64[-3] = Const CInt64(-3) v33:CInt64[3] = Const CInt64(3) - v27:CInt64[-3] = GuardLess v32, v33 - v28:CInt64 = AdjustBounds v27, v33 + v28:CInt64 = AdjustBounds v32, v33 v29:CInt64[0] = Const CInt64(0) v30:CInt64 = GuardGreaterEq v28, v29 v31:BasicObject = ArrayAref v11, v30 @@ -6556,8 +6551,7 @@ mod hir_opt_tests { PatchPoint MethodRedefined(Array@0x1008, []@0x1010, cme:0x1018) v32:CInt64[-10] = Const CInt64(-10) v33:CInt64[3] = Const CInt64(3) - v27:CInt64[-10] = GuardLess v32, v33 - v28:CInt64 = AdjustBounds v27, v33 + v28:CInt64 = AdjustBounds v32, v33 v29:CInt64[0] = Const CInt64(0) v30:CInt64 = GuardGreaterEq v28, v29 v31:BasicObject = ArrayAref v11, v30 @@ -6587,12 +6581,7 @@ mod hir_opt_tests { v13:Fixnum[10] = Const Value(10) PatchPoint NoSingletonClass(Array@0x1008) PatchPoint MethodRedefined(Array@0x1008, []@0x1010, cme:0x1018) - v32:CInt64[10] = Const CInt64(10) - v33:CInt64[3] = Const CInt64(3) - v27:CInt64[10] = GuardLess v32, v33 - v34:NilClass = Const Value(nil) - CheckInterrupts - Return v34 + SideExit GuardLess "); } @@ -13664,12 +13653,9 @@ mod hir_opt_tests { v12:Fixnum[0] = Const Value(0) PatchPoint NoSingletonClass(Array@0x1010) PatchPoint MethodRedefined(Array@0x1010, []@0x1018, cme:0x1020) - v34:CInt64[0] = Const CInt64(0) - v28:CInt64 = ArrayLength v23 - v29:CInt64[0] = GuardLess v34, v28 - v35:ModuleExact[VALUE(0x1048)] = Const Value(VALUE(0x1048)) + v36:ModuleExact[VALUE(0x1048)] = Const Value(VALUE(0x1048)) CheckInterrupts - Return v35 + Return v36 "); } From c34cc402a378be826714aae67af5d494107d9814 Mon Sep 17 00:00:00 2001 From: Ibrahim Awwal Date: Tue, 19 May 2026 01:02:41 +0000 Subject: [PATCH 04/11] YJIT: Fix local register mapping overflow Previously, local indices greater than 255 were truncated when converted to RegOpnd::Local. This could make a high-index local alias a tracked low-index local in the register mapping and produce incorrect values after compilation. Cap overflowing indices to MAX_CTX_LOCALS. RegMapping treats that index as untrackable because it is just outside the tracked local range. Fixes [Bug #22074]. Co-authored-by: Codex <199175422+chatgpt-connector[bot]@users.noreply.github.com> --- bootstraptest/test_yjit.rb | 20 ++++++++++++++++++++ yjit/src/backend/ir.rs | 6 ++++-- yjit/src/core.rs | 2 +- 3 files changed, 25 insertions(+), 3 deletions(-) diff --git a/bootstraptest/test_yjit.rb b/bootstraptest/test_yjit.rb index f19629ec0e4e85..e9ce905e2c7073 100644 --- a/bootstraptest/test_yjit.rb +++ b/bootstraptest/test_yjit.rb @@ -5535,3 +5535,23 @@ def compiled_method # Resume the fiber — compiled_method's iseq must still be valid fiber.resume.to_s } + +# regression test for register mapping of methods with over 256 locals +# [Bug #22074] +assert_equal "ok", %q{ + source = +"def many_locals\n" + source << " total = 0\n" + + 128.times do |i| + source << " y#{i} = 1\n" + source << " x#{i} = Object.new\n" + end + + source << " total += 1\n" + source << " raise total.inspect unless total == 1\n" + source << "end\n" + + eval(source) + many_locals + "ok" +} diff --git a/yjit/src/backend/ir.rs b/yjit/src/backend/ir.rs index 3fb67bc7cc1584..e14f3f9cb28e95 100644 --- a/yjit/src/backend/ir.rs +++ b/yjit/src/backend/ir.rs @@ -6,7 +6,7 @@ use crate::codegen::{gen_counted_exit, gen_outlined_exit}; use crate::cruby::{vm_stack_canary, SIZEOF_VALUE_I32, VALUE, VM_ENV_DATA_SIZE}; use crate::virtualmem::CodePtr; use crate::asm::{CodeBlock, OutlinedCb}; -use crate::core::{Context, RegMapping, RegOpnd, MAX_CTX_TEMPS}; +use crate::core::{Context, RegMapping, RegOpnd, MAX_CTX_LOCALS, MAX_CTX_TEMPS}; use crate::options::*; use crate::stats::*; @@ -242,7 +242,9 @@ impl Opnd let last_idx = stack_size as i32 + VM_ENV_DATA_SIZE as i32 - 1; assert!(last_idx <= idx, "Local index {} must be >= last local index {}", idx, last_idx); assert!(idx <= last_idx + num_locals as i32, "Local index {} must be < last local index {} + local size {}", idx, last_idx, num_locals); - RegOpnd::Local((last_idx + num_locals as i32 - idx) as u8) + // Indices that don't fit in u8 are capped to MAX_CTX_LOCALS, which is untrackable. + let local_idx = last_idx + num_locals as i32 - idx; + RegOpnd::Local(local_idx.try_into().unwrap_or(MAX_CTX_LOCALS as u8)) } else { assert!(idx < stack_size as i32); RegOpnd::Stack((stack_size as i32 - idx - 1) as u8) diff --git a/yjit/src/core.rs b/yjit/src/core.rs index edb7db3acc5455..d08cd1fb26fac3 100644 --- a/yjit/src/core.rs +++ b/yjit/src/core.rs @@ -34,7 +34,7 @@ use crate::invariants::*; pub const MAX_CTX_TEMPS: usize = 8; // Maximum number of local variable types or registers we keep track of -const MAX_CTX_LOCALS: usize = 8; +pub const MAX_CTX_LOCALS: usize = 8; /// An index into `ISEQ_BODY(iseq)->iseq_encoded`. Points /// to a YARV instruction or an instruction operand. From 214cfad69c40a6300235c78a5041465055914ce9 Mon Sep 17 00:00:00 2001 From: Max Bernstein Date: Wed, 20 May 2026 12:25:42 -0400 Subject: [PATCH 05/11] ZJIT: Split Class type into ClassExact and ClassSubclass Add ClassExact and ClassSubclass bits to the HIR type lattice, with Class = ClassExact | ClassSubclass. Remove the special-case dispatch on RUBY_T_CLASS and RUBY_T_MODULE in from_value; classification now flows through the standard exact/subclass class lookup, so e.g. Kernel (whose class_of returns its singleton class) is recognized as ModuleSubclass rather than ModuleExact. --- zjit/src/hir/opt_tests.rs | 86 +++++++++++++++---------------- zjit/src/hir/tests.rs | 6 +-- zjit/src/hir_type/gen_hir_type.rb | 6 +-- zjit/src/hir_type/hir_type.inc.rs | 77 +++++++++++++++------------ zjit/src/hir_type/mod.rs | 20 +------ 5 files changed, 94 insertions(+), 101 deletions(-) diff --git a/zjit/src/hir/opt_tests.rs b/zjit/src/hir/opt_tests.rs index 172cb92b7b9596..db00e018281ca1 100644 --- a/zjit/src/hir/opt_tests.rs +++ b/zjit/src/hir/opt_tests.rs @@ -3265,7 +3265,7 @@ mod hir_opt_tests { bb3(v6:BasicObject): PatchPoint SingleRactorMode PatchPoint StableConstantNames(0x1000, C) - v18:Class[C@0x1008] = Const Value(VALUE(0x1008)) + v18:ClassSubclass[C@0x1008] = Const Value(VALUE(0x1008)) CheckInterrupts Return v18 "); @@ -3290,13 +3290,13 @@ mod hir_opt_tests { bb3(v6:BasicObject): PatchPoint SingleRactorMode PatchPoint StableConstantNames(0x1000, String) - v26:Class[String@0x1008] = Const Value(VALUE(0x1008)) + v26:ClassSubclass[String@0x1008] = Const Value(VALUE(0x1008)) PatchPoint StableConstantNames(0x1010, Class) - v29:Class[Class@0x1018] = Const Value(VALUE(0x1018)) + v29:ClassSubclass[Class@0x1018] = Const Value(VALUE(0x1018)) PatchPoint StableConstantNames(0x1020, Module) - v32:Class[Module@0x1028] = Const Value(VALUE(0x1028)) + v32:ClassSubclass[Module@0x1028] = Const Value(VALUE(0x1028)) PatchPoint StableConstantNames(0x1030, BasicObject) - v35:Class[BasicObject@0x1038] = Const Value(VALUE(0x1038)) + v35:ClassSubclass[BasicObject@0x1038] = Const Value(VALUE(0x1038)) v18:ArrayExact = NewArray v26, v29, v32, v35 CheckInterrupts Return v18 @@ -3324,7 +3324,7 @@ mod hir_opt_tests { PatchPoint StableConstantNames(0x1000, Enumerable) v22:ModuleExact[Enumerable@0x1008] = Const Value(VALUE(0x1008)) PatchPoint StableConstantNames(0x1010, Kernel) - v25:ModuleExact[Kernel@0x1018] = Const Value(VALUE(0x1018)) + v25:ModuleSubclass[Kernel@0x1018] = Const Value(VALUE(0x1018)) v14:ArrayExact = NewArray v22, v25 CheckInterrupts Return v14 @@ -3577,7 +3577,7 @@ mod hir_opt_tests { v20:ModuleExact[M@0x1008] = Const Value(VALUE(0x1008)) PatchPoint NoSingletonClass(Module@0x1010) PatchPoint MethodRedefined(Module@0x1010, class@0x1018, cme:0x1020) - v25:Class[Module@0x1010] = Const Value(VALUE(0x1010)) + v25:ClassSubclass[Module@0x1010] = Const Value(VALUE(0x1010)) CheckInterrupts Return v25 "); @@ -4391,7 +4391,7 @@ mod hir_opt_tests { bb3(v6:BasicObject): PatchPoint SingleRactorMode PatchPoint StableConstantNames(0x1000, Kernel) - v18:ModuleExact[Kernel@0x1008] = Const Value(VALUE(0x1008)) + v18:ModuleSubclass[Kernel@0x1008] = Const Value(VALUE(0x1008)) CheckInterrupts Return v18 "); @@ -4422,7 +4422,7 @@ mod hir_opt_tests { bb3(v6:BasicObject): PatchPoint SingleRactorMode PatchPoint StableConstantNames(0x1000, Foo::Bar::C) - v18:Class[Foo::Bar::C@0x1008] = Const Value(VALUE(0x1008)) + v18:ClassSubclass[Foo::Bar::C@0x1008] = Const Value(VALUE(0x1008)) CheckInterrupts Return v18 "); @@ -4448,7 +4448,7 @@ mod hir_opt_tests { bb3(v6:BasicObject): PatchPoint SingleRactorMode PatchPoint StableConstantNames(0x1000, C) - v43:Class[C@0x1008] = Const Value(VALUE(0x1008)) + v43:ClassSubclass[C@0x1008] = Const Value(VALUE(0x1008)) v12:NilClass = Const Value(nil) PatchPoint MethodRedefined(C@0x1008, new@0x1009, cme:0x1010) v46:ObjectSubclass[class_exact:C] = ObjectAllocClass C:VALUE(0x1008) @@ -4484,7 +4484,7 @@ mod hir_opt_tests { bb3(v6:BasicObject): PatchPoint SingleRactorMode PatchPoint StableConstantNames(0x1000, C) - v46:Class[C@0x1008] = Const Value(VALUE(0x1008)) + v46:ClassSubclass[C@0x1008] = Const Value(VALUE(0x1008)) v12:NilClass = Const Value(nil) v15:Fixnum[1] = Const Value(1) PatchPoint MethodRedefined(C@0x1008, new@0x1009, cme:0x1010) @@ -4516,7 +4516,7 @@ mod hir_opt_tests { bb3(v6:BasicObject): PatchPoint SingleRactorMode PatchPoint StableConstantNames(0x1000, Object) - v43:Class[Object@0x1008] = Const Value(VALUE(0x1008)) + v43:ClassSubclass[Object@0x1008] = Const Value(VALUE(0x1008)) v12:NilClass = Const Value(nil) PatchPoint MethodRedefined(Object@0x1008, new@0x1009, cme:0x1010) v46:ObjectExact = ObjectAllocClass Object:VALUE(0x1008) @@ -4547,7 +4547,7 @@ mod hir_opt_tests { bb3(v6:BasicObject): PatchPoint SingleRactorMode PatchPoint StableConstantNames(0x1000, BasicObject) - v43:Class[BasicObject@0x1008] = Const Value(VALUE(0x1008)) + v43:ClassSubclass[BasicObject@0x1008] = Const Value(VALUE(0x1008)) v12:NilClass = Const Value(nil) PatchPoint MethodRedefined(BasicObject@0x1008, new@0x1009, cme:0x1010) v46:BasicObjectExact = ObjectAllocClass BasicObject:VALUE(0x1008) @@ -4578,7 +4578,7 @@ mod hir_opt_tests { bb3(v6:BasicObject): PatchPoint SingleRactorMode PatchPoint StableConstantNames(0x1000, Hash) - v43:Class[Hash@0x1008] = Const Value(VALUE(0x1008)) + v43:ClassSubclass[Hash@0x1008] = Const Value(VALUE(0x1008)) v12:NilClass = Const Value(nil) PatchPoint MethodRedefined(Hash@0x1008, new@0x1009, cme:0x1010) v46:HashExact = ObjectAllocClass Hash:VALUE(0x1008) @@ -4611,7 +4611,7 @@ mod hir_opt_tests { bb3(v6:BasicObject): PatchPoint SingleRactorMode PatchPoint StableConstantNames(0x1000, Array) - v46:Class[Array@0x1008] = Const Value(VALUE(0x1008)) + v46:ClassSubclass[Array@0x1008] = Const Value(VALUE(0x1008)) v12:NilClass = Const Value(nil) v15:Fixnum[1] = Const Value(1) PatchPoint MethodRedefined(Array@0x1008, new@0x1009, cme:0x1010) @@ -4641,7 +4641,7 @@ mod hir_opt_tests { bb3(v6:BasicObject): PatchPoint SingleRactorMode PatchPoint StableConstantNames(0x1000, Set) - v43:Class[Set@0x1008] = Const Value(VALUE(0x1008)) + v43:ClassSubclass[Set@0x1008] = Const Value(VALUE(0x1008)) v12:NilClass = Const Value(nil) PatchPoint MethodRedefined(Set@0x1008, new@0x1009, cme:0x1010) v17:HeapBasicObject = ObjectAlloc v43 @@ -4673,7 +4673,7 @@ mod hir_opt_tests { bb3(v6:BasicObject): PatchPoint SingleRactorMode PatchPoint StableConstantNames(0x1000, String) - v43:Class[String@0x1008] = Const Value(VALUE(0x1008)) + v43:ClassSubclass[String@0x1008] = Const Value(VALUE(0x1008)) v12:NilClass = Const Value(nil) PatchPoint MethodRedefined(String@0x1008, new@0x1009, cme:0x1010) PatchPoint MethodRedefined(Class@0x1038, new@0x1009, cme:0x1010) @@ -4702,7 +4702,7 @@ mod hir_opt_tests { bb3(v6:BasicObject): PatchPoint SingleRactorMode PatchPoint StableConstantNames(0x1000, Regexp) - v47:Class[Regexp@0x1008] = Const Value(VALUE(0x1008)) + v47:ClassSubclass[Regexp@0x1008] = Const Value(VALUE(0x1008)) v12:NilClass = Const Value(nil) v15:StringExact[VALUE(0x1010)] = Const Value(VALUE(0x1010)) v16:StringExact = StringCopy v15 @@ -4736,7 +4736,7 @@ mod hir_opt_tests { bb3(v6:BasicObject): PatchPoint SingleRactorMode PatchPoint StableConstantNames(0x1000, C) - v20:Class[C@0x1008] = Const Value(VALUE(0x1008)) + v20:ClassSubclass[C@0x1008] = Const Value(VALUE(0x1008)) PatchPoint MethodRedefined(Class@0x1010, allocate@0x1018, cme:0x1020) v23:ObjectSubclass[class_exact:C] = ObjectAllocClass C:VALUE(0x1008) CheckInterrupts @@ -4766,7 +4766,7 @@ mod hir_opt_tests { bb3(v6:BasicObject): PatchPoint SingleRactorMode PatchPoint StableConstantNames(0x1000, C) - v22:Class[C@0x1008] = Const Value(VALUE(0x1008)) + v22:ClassSubclass[C@0x1008] = Const Value(VALUE(0x1008)) v12:Fixnum[1] = Const Value(1) v14:BasicObject = Send v22, :allocate, v12 # SendFallbackReason: SendWithoutBlock: unsupported method type Cfunc CheckInterrupts @@ -4796,7 +4796,7 @@ mod hir_opt_tests { bb3(v6:BasicObject): PatchPoint SingleRactorMode PatchPoint StableConstantNames(0x1000, SC) - v20:Class[Class@0x1008] = Const Value(VALUE(0x1008)) + v20:ClassSubclass[Class@0x1008] = Const Value(VALUE(0x1008)) PatchPoint MethodRedefined(Class@0x1010, allocate@0x1018, cme:0x1020) v23:BasicObject = CCallWithFrame v20, :Class.allocate@0x1048 CheckInterrupts @@ -6829,7 +6829,7 @@ mod hir_opt_tests { bb3(v6:BasicObject): PatchPoint SingleRactorMode PatchPoint StableConstantNames(0x1000, Foo) - v22:Class[Foo@0x1008] = Const Value(VALUE(0x1008)) + v22:ClassSubclass[Foo@0x1008] = Const Value(VALUE(0x1008)) v12:Fixnum[100] = Const Value(100) PatchPoint MethodRedefined(Class@0x1010, identity@0x1018, cme:0x1020) CheckInterrupts @@ -9711,7 +9711,7 @@ mod hir_opt_tests { bb3(v6:BasicObject): PatchPoint SingleRactorMode PatchPoint StableConstantNames(0x1000, Thread) - v20:Class[Thread@0x1008] = Const Value(VALUE(0x1008)) + v20:ClassSubclass[Thread@0x1008] = Const Value(VALUE(0x1008)) PatchPoint MethodRedefined(Class@0x1010, current@0x1018, cme:0x1020) v23:CPtr = LoadEC v24:CPtr = LoadField v23, :thread_ptr@0x1048 @@ -12968,7 +12968,7 @@ mod hir_opt_tests { bb3(v9:BasicObject, v10:BasicObject): PatchPoint SingleRactorMode PatchPoint StableConstantNames(0x1008, String) - v27:Class[String@0x1010] = Const Value(VALUE(0x1010)) + v27:ClassSubclass[String@0x1010] = Const Value(VALUE(0x1010)) PatchPoint NoEPEscape(test) PatchPoint MethodRedefined(Class@0x1018, ===@0x1020, cme:0x1028) v30:BoolExact = IsA v10, v27 @@ -12999,7 +12999,7 @@ mod hir_opt_tests { bb3(v9:BasicObject, v10:BasicObject): PatchPoint SingleRactorMode PatchPoint StableConstantNames(0x1008, Kernel) - v27:ModuleExact[Kernel@0x1010] = Const Value(VALUE(0x1010)) + v27:ModuleSubclass[Kernel@0x1010] = Const Value(VALUE(0x1010)) PatchPoint NoEPEscape(test) PatchPoint MethodRedefined(Module@0x1018, ===@0x1020, cme:0x1028) v30:BoolExact = CCall v27, :Module#===@0x1050, v10 @@ -13030,7 +13030,7 @@ mod hir_opt_tests { bb3(v9:BasicObject, v10:BasicObject): PatchPoint SingleRactorMode PatchPoint StableConstantNames(0x1008, String) - v25:Class[String@0x1010] = Const Value(VALUE(0x1010)) + v25:ClassSubclass[String@0x1010] = Const Value(VALUE(0x1010)) PatchPoint NoSingletonClass(String@0x1010) PatchPoint MethodRedefined(String@0x1010, is_a?@0x1011, cme:0x1018) v29:StringExact = GuardType v10, StringExact @@ -13062,7 +13062,7 @@ mod hir_opt_tests { bb3(v9:BasicObject, v10:BasicObject): PatchPoint SingleRactorMode PatchPoint StableConstantNames(0x1008, Kernel) - v25:ModuleExact[Kernel@0x1010] = Const Value(VALUE(0x1010)) + v25:ModuleSubclass[Kernel@0x1010] = Const Value(VALUE(0x1010)) PatchPoint NoSingletonClass(String@0x1018) PatchPoint MethodRedefined(String@0x1018, is_a?@0x1020, cme:0x1028) v29:StringExact = GuardType v10, StringExact @@ -13097,7 +13097,7 @@ mod hir_opt_tests { bb3(v9:BasicObject, v10:BasicObject): PatchPoint SingleRactorMode PatchPoint StableConstantNames(0x1008, Integer) - v29:Class[Integer@0x1010] = Const Value(VALUE(0x1010)) + v29:ClassSubclass[Integer@0x1010] = Const Value(VALUE(0x1010)) PatchPoint NoSingletonClass(String@0x1018) PatchPoint MethodRedefined(String@0x1018, is_a?@0x1020, cme:0x1028) v33:StringExact = GuardType v10, StringExact @@ -13132,7 +13132,7 @@ mod hir_opt_tests { bb3(v9:BasicObject, v10:BasicObject): PatchPoint SingleRactorMode PatchPoint StableConstantNames(0x1008, Integer) - v31:Class[Integer@0x1010] = Const Value(VALUE(0x1010)) + v31:ClassSubclass[Integer@0x1010] = Const Value(VALUE(0x1010)) PatchPoint NoEPEscape(test) PatchPoint MethodRedefined(Class@0x1018, ===@0x1020, cme:0x1028) v23:Fixnum[5] = Const Value(5) @@ -13163,7 +13163,7 @@ mod hir_opt_tests { bb3(v9:BasicObject, v10:BasicObject): PatchPoint SingleRactorMode PatchPoint StableConstantNames(0x1008, String) - v25:Class[String@0x1010] = Const Value(VALUE(0x1010)) + v25:ClassSubclass[String@0x1010] = Const Value(VALUE(0x1010)) PatchPoint NoSingletonClass(String@0x1010) PatchPoint MethodRedefined(String@0x1010, kind_of?@0x1011, cme:0x1018) v29:StringExact = GuardType v10, StringExact @@ -13195,7 +13195,7 @@ mod hir_opt_tests { bb3(v9:BasicObject, v10:BasicObject): PatchPoint SingleRactorMode PatchPoint StableConstantNames(0x1008, Kernel) - v25:ModuleExact[Kernel@0x1010] = Const Value(VALUE(0x1010)) + v25:ModuleSubclass[Kernel@0x1010] = Const Value(VALUE(0x1010)) PatchPoint NoSingletonClass(String@0x1018) PatchPoint MethodRedefined(String@0x1018, kind_of?@0x1020, cme:0x1028) v29:StringExact = GuardType v10, StringExact @@ -13230,7 +13230,7 @@ mod hir_opt_tests { bb3(v9:BasicObject, v10:BasicObject): PatchPoint SingleRactorMode PatchPoint StableConstantNames(0x1008, Integer) - v29:Class[Integer@0x1010] = Const Value(VALUE(0x1010)) + v29:ClassSubclass[Integer@0x1010] = Const Value(VALUE(0x1010)) PatchPoint NoSingletonClass(String@0x1018) PatchPoint MethodRedefined(String@0x1018, kind_of?@0x1020, cme:0x1028) v33:StringExact = GuardType v10, StringExact @@ -13260,7 +13260,7 @@ mod hir_opt_tests { v10:Fixnum[5] = Const Value(5) PatchPoint SingleRactorMode PatchPoint StableConstantNames(0x1000, Integer) - v22:Class[Integer@0x1008] = Const Value(VALUE(0x1008)) + v22:ClassSubclass[Integer@0x1008] = Const Value(VALUE(0x1008)) PatchPoint MethodRedefined(Integer@0x1008, is_a?@0x1009, cme:0x1010) v26:TrueClass = Const Value(true) CheckInterrupts @@ -13288,7 +13288,7 @@ mod hir_opt_tests { v10:Fixnum[5] = Const Value(5) PatchPoint SingleRactorMode PatchPoint StableConstantNames(0x1000, String) - v22:Class[String@0x1008] = Const Value(VALUE(0x1008)) + v22:ClassSubclass[String@0x1008] = Const Value(VALUE(0x1008)) PatchPoint MethodRedefined(Integer@0x1010, is_a?@0x1018, cme:0x1020) v26:FalseClass = Const Value(false) CheckInterrupts @@ -13319,7 +13319,7 @@ mod hir_opt_tests { PatchPoint StableConstantNames(0x1000, O) v22:ArraySubclass[VALUE(0x1008)] = Const Value(VALUE(0x1008)) PatchPoint StableConstantNames(0x1010, Array) - v25:Class[Array@0x1018] = Const Value(VALUE(0x1018)) + v25:ClassSubclass[Array@0x1018] = Const Value(VALUE(0x1018)) PatchPoint NoSingletonClass(C@0x1020) PatchPoint MethodRedefined(C@0x1020, is_a?@0x1028, cme:0x1030) v30:TrueClass = Const Value(true) @@ -13351,7 +13351,7 @@ mod hir_opt_tests { PatchPoint StableConstantNames(0x1000, O) v22:ObjectSubclass[VALUE(0x1008)] = Const Value(VALUE(0x1008)) PatchPoint StableConstantNames(0x1010, C) - v25:Class[C@0x1018] = Const Value(VALUE(0x1018)) + v25:ClassSubclass[C@0x1018] = Const Value(VALUE(0x1018)) PatchPoint NoSingletonClass(C@0x1018) PatchPoint MethodRedefined(C@0x1018, is_a?@0x1019, cme:0x1020) v30:TrueClass = Const Value(true) @@ -13382,7 +13382,7 @@ mod hir_opt_tests { PatchPoint StableConstantNames(0x1000, O) v22:StaticSymbol[:my_static_symbol] = Const Value(VALUE(0x1008)) PatchPoint StableConstantNames(0x1010, Symbol) - v25:Class[Symbol@0x1018] = Const Value(VALUE(0x1018)) + v25:ClassSubclass[Symbol@0x1018] = Const Value(VALUE(0x1018)) PatchPoint MethodRedefined(Symbol@0x1018, is_a?@0x1019, cme:0x1020) v29:TrueClass = Const Value(true) CheckInterrupts @@ -13505,7 +13505,7 @@ mod hir_opt_tests { PatchPoint NoSingletonClass(C@0x1000) PatchPoint MethodRedefined(C@0x1000, class@0x1008, cme:0x1010) v43:ObjectSubclass[class_exact:C] = GuardType v6, ObjectSubclass[class_exact:C] - v46:Class[C@0x1000] = Const Value(VALUE(0x1000)) + v46:ClassSubclass[C@0x1000] = Const Value(VALUE(0x1000)) v13:StaticSymbol[:_lex_actions] = Const Value(VALUE(0x1038)) v15:TrueClass = Const Value(true) PatchPoint MethodRedefined(Class@0x1040, respond_to?@0x1048, cme:0x1050) @@ -13541,7 +13541,7 @@ mod hir_opt_tests { PatchPoint NoSingletonClass(C@0x1008) PatchPoint MethodRedefined(C@0x1008, class@0x1010, cme:0x1018) v25:ObjectSubclass[class_exact:C] = GuardType v10, ObjectSubclass[class_exact:C] - v28:Class[C@0x1008] = Const Value(VALUE(0x1008)) + v28:ClassSubclass[C@0x1008] = Const Value(VALUE(0x1008)) PatchPoint MethodRedefined(Class@0x1040, name@0x1048, cme:0x1050) v31:StringExact|NilClass = CCall v28, :Module#name@0x1078 CheckInterrupts @@ -13573,7 +13573,7 @@ mod hir_opt_tests { PatchPoint NoSingletonClass(C@0x1008) PatchPoint MethodRedefined(C@0x1008, class@0x1010, cme:0x1018) v23:ObjectSubclass[class_exact:C] = GuardType v10, ObjectSubclass[class_exact:C] - v26:Class[C@0x1008] = Const Value(VALUE(0x1008)) + v26:ClassSubclass[C@0x1008] = Const Value(VALUE(0x1008)) CheckInterrupts Return v26 "); @@ -13598,7 +13598,7 @@ mod hir_opt_tests { bb3(v6:BasicObject): v10:Fixnum[5] = Const Value(5) PatchPoint MethodRedefined(Integer@0x1000, class@0x1008, cme:0x1010) - v21:Class[Integer@0x1000] = Const Value(VALUE(0x1000)) + v21:ClassSubclass[Integer@0x1000] = Const Value(VALUE(0x1000)) CheckInterrupts Return v21 "); @@ -13623,7 +13623,7 @@ mod hir_opt_tests { bb3(v6:BasicObject): PatchPoint MethodRedefined(Object@0x1000, class@0x1008, cme:0x1010) v18:ObjectSubclass[class_exact*:Object@VALUE(0x1000)] = GuardType v6, ObjectSubclass[class_exact*:Object@VALUE(0x1000)] - v21:Class[Object@0x1038] = Const Value(VALUE(0x1038)) + v21:ClassSubclass[Object@0x1038] = Const Value(VALUE(0x1038)) CheckInterrupts Return v21 "); @@ -13702,7 +13702,7 @@ mod hir_opt_tests { SetLocal :formatted, l0, EP@3, v16 PatchPoint SingleRactorMode SetIvar v15, :@formatted, v16 - v47:Class[VMFrozenCore] = Const Value(VALUE(0x1008)) + v47:ClassSubclass[VMFrozenCore] = Const Value(VALUE(0x1008)) PatchPoint MethodRedefined(Class@0x1010, lambda@0x1018, cme:0x1020) v62:BasicObject = CCallWithFrame v47, :RubyVM::FrozenCore.lambda@0x1048, block=0x1050 v50:CPtr = GetEP 0 diff --git a/zjit/src/hir/tests.rs b/zjit/src/hir/tests.rs index 566cf7aeb7cf7a..00c6f70550d2d9 100644 --- a/zjit/src/hir/tests.rs +++ b/zjit/src/hir/tests.rs @@ -2384,11 +2384,11 @@ pub(crate) mod hir_build_tests { v7:BasicObject = LoadArg :a@1 Jump bb3(v6, v7) bb3(v9:BasicObject, v10:BasicObject): - v15:Class[VMFrozenCore] = Const Value(VALUE(0x1008)) + v15:ClassSubclass[VMFrozenCore] = Const Value(VALUE(0x1008)) v17:HashExact = NewHash PatchPoint NoEPEscape(test) v22:BasicObject = Send v15, :core#hash_merge_kwd, v17, v10 # SendFallbackReason: Uncategorized(opt_send_without_block) - v24:Class[VMFrozenCore] = Const Value(VALUE(0x1008)) + v24:ClassSubclass[VMFrozenCore] = Const Value(VALUE(0x1008)) v27:StaticSymbol[:b] = Const Value(VALUE(0x1010)) v29:Fixnum[1] = Const Value(1) v31:BasicObject = Send v24, :core#hash_merge_ptr, v22, v27, v29 # SendFallbackReason: Uncategorized(opt_send_without_block) @@ -4360,7 +4360,7 @@ pub(crate) mod hir_build_tests { v4:BasicObject = LoadArg :self@0 Jump bb3(v4) bb3(v6:BasicObject): - v10:Class[VMFrozenCore] = Const Value(VALUE(0x1000)) + v10:ClassSubclass[VMFrozenCore] = Const Value(VALUE(0x1000)) v12:BasicObject = PutSpecialObject CBase v14:StaticSymbol[:aliased] = Const Value(VALUE(0x1008)) v16:StaticSymbol[:__callee__] = Const Value(VALUE(0x1010)) diff --git a/zjit/src/hir_type/gen_hir_type.rb b/zjit/src/hir_type/gen_hir_type.rb index e9d925ba0f5b13..41f96a7a82e4d4 100644 --- a/zjit/src/hir_type/gen_hir_type.rb +++ b/zjit/src/hir_type/gen_hir_type.rb @@ -75,8 +75,8 @@ def to_graphviz type, f # Define a new type that can be subclassed (most of them). # If c_name is given, mark the rb_cXYZ object as equivalent to this exact type. -def base_type name, c_name: nil - type = $object.subtype name +def base_type name, base: $object, c_name: nil + type = base.subtype name exact = type.subtype(name+"Exact") subclass = type.subtype(name+"Subclass") if c_name @@ -111,7 +111,7 @@ def final_type name, base: $object, c_name: nil module_class, _ = base_type "Module", c_name: "rb_cModule" # Class cannot be subclassed by doing `class Sub < Class`, # but every metaclass is a subclass of `Class`. It's not final. -module_class.subtype "Class" +base_type "Class", base: module_class, c_name: "rb_cClass" numeric, _ = base_type "Numeric", c_name: "rb_cNumeric" diff --git a/zjit/src/hir_type/hir_type.inc.rs b/zjit/src/hir_type/hir_type.inc.rs index 8b95914ddf180a..f4c3bf348916b0 100644 --- a/zjit/src/hir_type/hir_type.inc.rs +++ b/zjit/src/hir_type/hir_type.inc.rs @@ -9,7 +9,7 @@ mod bits { pub const BasicObjectSubclass: u64 = 1u64 << 3; pub const Bignum: u64 = 1u64 << 4; pub const BoolExact: u64 = FalseClass | TrueClass; - pub const BuiltinExact: u64 = ArrayExact | BasicObjectExact | FalseClass | Float | HashExact | Integer | ModuleExact | NilClass | NumericExact | ObjectExact | RangeExact | RegexpExact | SetExact | StringExact | Symbol | TrueClass; + pub const BuiltinExact: u64 = ArrayExact | BasicObjectExact | ClassExact | FalseClass | Float | HashExact | Integer | ModuleExact | NilClass | NumericExact | ObjectExact | RangeExact | RegexpExact | SetExact | StringExact | Symbol | TrueClass; pub const CAttrIndex: u64 = 1u64 << 5; pub const CBool: u64 = 1u64 << 6; pub const CDouble: u64 = 1u64 << 7; @@ -29,54 +29,56 @@ mod bits { pub const CUnsigned: u64 = CAttrIndex | CShape | CUInt16 | CUInt32 | CUInt64 | CUInt8; pub const CValue: u64 = CBool | CDouble | CInt | CNull | CPtr; pub const CallableMethodEntry: u64 = 1u64 << 19; - pub const Class: u64 = 1u64 << 20; - pub const DynamicSymbol: u64 = 1u64 << 21; + pub const Class: u64 = ClassExact | ClassSubclass; + pub const ClassExact: u64 = 1u64 << 20; + pub const ClassSubclass: u64 = 1u64 << 21; + pub const DynamicSymbol: u64 = 1u64 << 22; pub const Empty: u64 = 0u64; - pub const FalseClass: u64 = 1u64 << 22; + pub const FalseClass: u64 = 1u64 << 23; pub const Falsy: u64 = FalseClass | NilClass; - pub const Fixnum: u64 = 1u64 << 23; + pub const Fixnum: u64 = 1u64 << 24; pub const Float: u64 = Flonum | HeapFloat; - pub const Flonum: u64 = 1u64 << 24; + pub const Flonum: u64 = 1u64 << 25; pub const Hash: u64 = HashExact | HashSubclass; - pub const HashExact: u64 = 1u64 << 25; - pub const HashSubclass: u64 = 1u64 << 26; + pub const HashExact: u64 = 1u64 << 26; + pub const HashSubclass: u64 = 1u64 << 27; pub const HeapBasicObject: u64 = BasicObject & !Immediate; - pub const HeapFloat: u64 = 1u64 << 27; + pub const HeapFloat: u64 = 1u64 << 28; pub const HeapObject: u64 = Object & !Immediate; pub const Immediate: u64 = FalseClass | Fixnum | Flonum | NilClass | StaticSymbol | TrueClass | Undef; pub const Integer: u64 = Bignum | Fixnum; pub const Module: u64 = Class | ModuleExact | ModuleSubclass; - pub const ModuleExact: u64 = 1u64 << 28; - pub const ModuleSubclass: u64 = 1u64 << 29; - pub const NilClass: u64 = 1u64 << 30; + pub const ModuleExact: u64 = 1u64 << 29; + pub const ModuleSubclass: u64 = 1u64 << 30; + pub const NilClass: u64 = 1u64 << 31; pub const NotNil: u64 = BasicObject & !NilClass; pub const Numeric: u64 = Float | Integer | NumericExact | NumericSubclass; - pub const NumericExact: u64 = 1u64 << 31; - pub const NumericSubclass: u64 = 1u64 << 32; + pub const NumericExact: u64 = 1u64 << 32; + pub const NumericSubclass: u64 = 1u64 << 33; pub const Object: u64 = Array | FalseClass | Hash | Module | NilClass | Numeric | ObjectExact | ObjectSubclass | Range | Regexp | Set | String | Symbol | TrueClass; - pub const ObjectExact: u64 = 1u64 << 33; - pub const ObjectSubclass: u64 = 1u64 << 34; + pub const ObjectExact: u64 = 1u64 << 34; + pub const ObjectSubclass: u64 = 1u64 << 35; pub const Range: u64 = RangeExact | RangeSubclass; - pub const RangeExact: u64 = 1u64 << 35; - pub const RangeSubclass: u64 = 1u64 << 36; + pub const RangeExact: u64 = 1u64 << 36; + pub const RangeSubclass: u64 = 1u64 << 37; pub const Regexp: u64 = RegexpExact | RegexpSubclass; - pub const RegexpExact: u64 = 1u64 << 37; - pub const RegexpSubclass: u64 = 1u64 << 38; + pub const RegexpExact: u64 = 1u64 << 38; + pub const RegexpSubclass: u64 = 1u64 << 39; pub const RubyValue: u64 = BasicObject | CallableMethodEntry | Undef; pub const Set: u64 = SetExact | SetSubclass; - pub const SetExact: u64 = 1u64 << 39; - pub const SetSubclass: u64 = 1u64 << 40; - pub const StaticSymbol: u64 = 1u64 << 41; + pub const SetExact: u64 = 1u64 << 40; + pub const SetSubclass: u64 = 1u64 << 41; + pub const StaticSymbol: u64 = 1u64 << 42; pub const String: u64 = StringExact | StringSubclass; - pub const StringExact: u64 = 1u64 << 42; - pub const StringSubclass: u64 = 1u64 << 43; - pub const Subclass: u64 = ArraySubclass | BasicObjectSubclass | HashSubclass | ModuleSubclass | NumericSubclass | ObjectSubclass | RangeSubclass | RegexpSubclass | SetSubclass | StringSubclass; + pub const StringExact: u64 = 1u64 << 43; + pub const StringSubclass: u64 = 1u64 << 44; + pub const Subclass: u64 = ArraySubclass | BasicObjectSubclass | ClassSubclass | HashSubclass | ModuleSubclass | NumericSubclass | ObjectSubclass | RangeSubclass | RegexpSubclass | SetSubclass | StringSubclass; pub const Symbol: u64 = DynamicSymbol | StaticSymbol; - pub const TrueClass: u64 = 1u64 << 44; + pub const TrueClass: u64 = 1u64 << 45; pub const Truthy: u64 = BasicObject & !Falsy; - pub const TypedTData: u64 = 1u64 << 45; - pub const Undef: u64 = 1u64 << 46; - pub const AllBitPatterns: [(&str, u64); 76] = [ + pub const TypedTData: u64 = 1u64 << 46; + pub const Undef: u64 = 1u64 << 47; + pub const AllBitPatterns: [(&str, u64); 78] = [ ("Any", Any), ("RubyValue", RubyValue), ("Immediate", Immediate), @@ -127,6 +129,8 @@ mod bits { ("FalseClass", FalseClass), ("DynamicSymbol", DynamicSymbol), ("Class", Class), + ("ClassSubclass", ClassSubclass), + ("ClassExact", ClassExact), ("CallableMethodEntry", CallableMethodEntry), ("CValue", CValue), ("CInt", CInt), @@ -154,7 +158,7 @@ mod bits { ("ArrayExact", ArrayExact), ("Empty", Empty), ]; - pub const NumTypeBits: u64 = 47; + pub const NumTypeBits: u64 = 48; } pub mod types { use super::*; @@ -188,6 +192,8 @@ pub mod types { pub const CValue: Type = Type::from_bits(bits::CValue); pub const CallableMethodEntry: Type = Type::from_bits(bits::CallableMethodEntry); pub const Class: Type = Type::from_bits(bits::Class); + pub const ClassExact: Type = Type::from_bits(bits::ClassExact); + pub const ClassSubclass: Type = Type::from_bits(bits::ClassSubclass); pub const DynamicSymbol: Type = Type::from_bits(bits::DynamicSymbol); pub const Empty: Type = Type::from_bits(bits::Empty); pub const FalseClass: Type = Type::from_bits(bits::FalseClass); @@ -234,7 +240,7 @@ pub mod types { pub const Truthy: Type = Type::from_bits(bits::Truthy); pub const TypedTData: Type = Type::from_bits(bits::TypedTData); pub const Undef: Type = Type::from_bits(bits::Undef); - pub const ExactBitsAndClass: [(u64, *const VALUE); 16] = [ + pub const ExactBitsAndClass: [(u64, *const VALUE); 17] = [ (bits::ObjectExact, &raw const crate::cruby::rb_cObject), (bits::BasicObjectExact, &raw const crate::cruby::rb_cBasicObject), (bits::StringExact, &raw const crate::cruby::rb_cString), @@ -244,6 +250,7 @@ pub mod types { (bits::SetExact, &raw const crate::cruby::rb_cSet), (bits::RegexpExact, &raw const crate::cruby::rb_cRegexp), (bits::ModuleExact, &raw const crate::cruby::rb_cModule), + (bits::ClassExact, &raw const crate::cruby::rb_cClass), (bits::NumericExact, &raw const crate::cruby::rb_cNumeric), (bits::Integer, &raw const crate::cruby::rb_cInteger), (bits::Float, &raw const crate::cruby::rb_cFloat), @@ -252,8 +259,9 @@ pub mod types { (bits::TrueClass, &raw const crate::cruby::rb_cTrueClass), (bits::FalseClass, &raw const crate::cruby::rb_cFalseClass), ]; - pub const SubclassBitsAndClass: [(u64, *const VALUE); 16] = [ + pub const SubclassBitsAndClass: [(u64, *const VALUE); 17] = [ (bits::ArraySubclass, &raw const crate::cruby::rb_cArray), + (bits::ClassSubclass, &raw const crate::cruby::rb_cClass), (bits::FalseClass, &raw const crate::cruby::rb_cFalseClass), (bits::Integer, &raw const crate::cruby::rb_cInteger), (bits::HashSubclass, &raw const crate::cruby::rb_cHash), @@ -270,8 +278,9 @@ pub mod types { (bits::ObjectSubclass, &raw const crate::cruby::rb_cObject), (bits::BasicObjectSubclass, &raw const crate::cruby::rb_cBasicObject), ]; - pub const InexactBitsAndClass: [(u64, *const VALUE); 16] = [ + pub const InexactBitsAndClass: [(u64, *const VALUE); 17] = [ (bits::Array, &raw const crate::cruby::rb_cArray), + (bits::Class, &raw const crate::cruby::rb_cClass), (bits::FalseClass, &raw const crate::cruby::rb_cFalseClass), (bits::Integer, &raw const crate::cruby::rb_cInteger), (bits::Hash, &raw const crate::cruby::rb_cHash), diff --git a/zjit/src/hir_type/mod.rs b/zjit/src/hir_type/mod.rs index 12e7986a397ae3..cdfc15a9355f64 100644 --- a/zjit/src/hir_type/mod.rs +++ b/zjit/src/hir_type/mod.rs @@ -2,14 +2,13 @@ #![allow(non_upper_case_globals)] use crate::cruby; -use crate::cruby::{rb_block_param_proxy, Qfalse, Qnil, Qtrue, RUBY_T_ARRAY, RUBY_T_CLASS, RUBY_T_HASH, RUBY_T_MODULE, RUBY_T_STRING, VALUE}; -use crate::cruby::{rb_cInteger, rb_cFloat, rb_cArray, rb_cHash, rb_cString, rb_cSymbol, rb_cRange, rb_cModule, rb_zjit_singleton_class_p}; +use crate::cruby::{rb_block_param_proxy, Qfalse, Qnil, Qtrue, RUBY_T_ARRAY, RUBY_T_HASH, RUBY_T_STRING, VALUE}; +use crate::cruby::{rb_cInteger, rb_cFloat, rb_cArray, rb_cHash, rb_cString, rb_cSymbol, rb_cRange, rb_zjit_singleton_class_p}; use crate::cruby::ClassRelationship; use crate::cruby::get_class_name; use crate::cruby::get_module_name; use crate::cruby::ruby_sym_to_rust_string; use crate::cruby::rb_mRubyVMFrozenCore; -use crate::cruby::rb_obj_class; use crate::hir::{Const, PtrPrintMap}; use crate::profile::ProfiledType; @@ -178,18 +177,6 @@ fn is_range_exact(val: VALUE) -> bool { val.class_of() == unsafe { rb_cRange } } -fn is_module_exact(val: VALUE) -> bool { - if val.builtin_type() != RUBY_T_MODULE { - return false; - } - - // For Class and Module instances, `class_of` will return the singleton class of the object. - // Using `rb_obj_class` will give us the actual class of the module so we can check if the - // object is an instance of Module, or an instance of Module subclass. - let klass = unsafe { rb_obj_class(val) }; - klass == unsafe { rb_cModule } -} - impl Type { /// Create a `Type` from the given integer. pub const fn fixnum(val: i64) -> Type { @@ -221,9 +208,6 @@ impl Type { if is_array_exact(val) { bits::ArrayExact } else if is_hash_exact(val) { bits::HashExact } else if is_string_exact(val) { bits::StringExact } - // Singleton classes - else if is_module_exact(val) { bits::ModuleExact } - else if val.builtin_type() == RUBY_T_CLASS { bits::Class } // Classes that have an immediate/heap split else if val.class_of() == unsafe { rb_cInteger } { bits::Bignum } else if val.class_of() == unsafe { rb_cFloat } { bits::HeapFloat } From cd28a282ccb37956edb11046c661e3e4786758c3 Mon Sep 17 00:00:00 2001 From: Max Bernstein Date: Wed, 20 May 2026 12:27:07 -0400 Subject: [PATCH 06/11] ZJIT: Prefer profile data over static type in resolve_receiver_type Try resolve_receiver_type_from_profile first and only fall back to the runtime exact class from the static type when no profile is available. Profile data carries shape information that the static type lacks, and duplicate guards can generally be eliminated downstream. --- zjit/src/hir.rs | 14 +- zjit/src/hir/opt_tests.rs | 304 +++++++++++++++++++------------------- zjit/src/hir/tests.rs | 6 +- 3 files changed, 166 insertions(+), 158 deletions(-) diff --git a/zjit/src/hir.rs b/zjit/src/hir.rs index a62e776d2fb70d..76ae1bb37f71e2 100644 --- a/zjit/src/hir.rs +++ b/zjit/src/hir.rs @@ -3323,10 +3323,18 @@ impl Function { /// - `StaticallyKnown` if the receiver's exact class is known at compile-time /// - Result of [`Self::resolve_receiver_type_from_profile`] if we need to check profile data fn resolve_receiver_type(&self, recv: InsnId, recv_type: Type, insn_idx: YarvInsnIdx) -> ReceiverTypeResolution { - if let Some(class) = recv_type.runtime_exact_ruby_class() { - return ReceiverTypeResolution::StaticallyKnown { class }; + match self.resolve_receiver_type_from_profile(recv, insn_idx) { + ReceiverTypeResolution::NoProfile => { + // Use known type information as a fallback because it doesn't have shape + // information (and we can generally eliminate duplicate guards). + if let Some(class) = recv_type.runtime_exact_ruby_class() { + ReceiverTypeResolution::StaticallyKnown { class } + } else { + ReceiverTypeResolution::NoProfile + } + } + resolution => resolution, } - self.resolve_receiver_type_from_profile(recv, insn_idx) } fn polymorphic_summary(&self, profiles: &ProfileOracle, recv: InsnId, insn_idx: YarvInsnIdx) -> Option { diff --git a/zjit/src/hir/opt_tests.rs b/zjit/src/hir/opt_tests.rs index db00e018281ca1..9950d9ce439929 100644 --- a/zjit/src/hir/opt_tests.rs +++ b/zjit/src/hir/opt_tests.rs @@ -222,13 +222,13 @@ mod hir_opt_tests { bb3(v9:BasicObject, v10:BasicObject): v14:Fixnum[0] = Const Value(0) PatchPoint MethodRedefined(Integer@0x1008, *@0x1010, cme:0x1018) - v35:Fixnum = GuardType v10, Fixnum - v44:Fixnum[0] = Const Value(0) - v45:Fixnum[0] = Const Value(0) - PatchPoint MethodRedefined(Integer@0x1008, +@0x1040, cme:0x1048) + v36:Fixnum = GuardType v10, Fixnum v46:Fixnum[0] = Const Value(0) + v47:Fixnum[0] = Const Value(0) + PatchPoint MethodRedefined(Integer@0x1008, +@0x1040, cme:0x1048) + v48:Fixnum[0] = Const Value(0) CheckInterrupts - Return v46 + Return v48 "); } @@ -608,9 +608,9 @@ mod hir_opt_tests { v10:Fixnum[4] = Const Value(4) v12:Fixnum[-7] = Const Value(-7) PatchPoint MethodRedefined(Integer@0x1000, &@0x1008, cme:0x1010) - v24:Fixnum[0] = Const Value(0) + v25:Fixnum[0] = Const Value(0) CheckInterrupts - Return v24 + Return v25 "); } @@ -637,9 +637,9 @@ mod hir_opt_tests { v10:Fixnum[-4] = Const Value(-4) v12:Fixnum[7] = Const Value(7) PatchPoint MethodRedefined(Integer@0x1000, &@0x1008, cme:0x1010) - v24:Fixnum[4] = Const Value(4) + v25:Fixnum[4] = Const Value(4) CheckInterrupts - Return v24 + Return v25 "); } @@ -666,9 +666,9 @@ mod hir_opt_tests { v10:Fixnum[4] = Const Value(4) v12:Fixnum[1] = Const Value(1) PatchPoint MethodRedefined(Integer@0x1000, |@0x1008, cme:0x1010) - v24:Fixnum[5] = Const Value(5) + v25:Fixnum[5] = Const Value(5) CheckInterrupts - Return v24 + Return v25 "); } @@ -695,9 +695,9 @@ mod hir_opt_tests { v10:Fixnum[-4] = Const Value(-4) v12:Fixnum[1] = Const Value(1) PatchPoint MethodRedefined(Integer@0x1000, |@0x1008, cme:0x1010) - v24:Fixnum[-3] = Const Value(-3) + v25:Fixnum[-3] = Const Value(-3) CheckInterrupts - Return v24 + Return v25 "); } @@ -724,9 +724,9 @@ mod hir_opt_tests { v10:Fixnum[4] = Const Value(4) v12:Fixnum[-1] = Const Value(-1) PatchPoint MethodRedefined(Integer@0x1000, |@0x1008, cme:0x1010) - v24:Fixnum[-1] = Const Value(-1) + v25:Fixnum[-1] = Const Value(-1) CheckInterrupts - Return v24 + Return v25 "); } @@ -1904,10 +1904,10 @@ mod hir_opt_tests { bb3(v9:BasicObject, v10:BasicObject): v14:Fixnum[1] = Const Value(1) PatchPoint MethodRedefined(Integer@0x1008, +@0x1010, cme:0x1018) - v26:Fixnum = GuardType v10, Fixnum - v27:Fixnum = FixnumAdd v14, v26 + v27:Fixnum = GuardType v10, Fixnum + v28:Fixnum = FixnumAdd v14, v27 CheckInterrupts - Return v27 + Return v28 "); } @@ -2104,10 +2104,10 @@ mod hir_opt_tests { bb3(v9:BasicObject, v10:BasicObject): v14:Fixnum[1] = Const Value(1) PatchPoint MethodRedefined(Integer@0x1008, <@0x1010, cme:0x1018) - v26:Fixnum = GuardType v10, Fixnum - v27:BoolExact = FixnumLt v14, v26 + v27:Fixnum = GuardType v10, Fixnum + v28:BoolExact = FixnumLt v14, v27 CheckInterrupts - Return v27 + Return v28 "); } @@ -3209,7 +3209,7 @@ mod hir_opt_tests { v29:ModuleExact[M@0x1008] = Const Value(VALUE(0x1008)) PatchPoint NoSingletonClass(Module@0x1010) PatchPoint MethodRedefined(Module@0x1010, name@0x1018, cme:0x1020) - v33:StringExact|NilClass = CCall v29, :Module#name@0x1048 + v34:StringExact|NilClass = CCall v29, :Module#name@0x1048 PatchPoint NoEPEscape(test) v21:Fixnum[1] = Const Value(1) CheckInterrupts @@ -3577,9 +3577,9 @@ mod hir_opt_tests { v20:ModuleExact[M@0x1008] = Const Value(VALUE(0x1008)) PatchPoint NoSingletonClass(Module@0x1010) PatchPoint MethodRedefined(Module@0x1010, class@0x1018, cme:0x1020) - v25:ClassSubclass[Module@0x1010] = Const Value(VALUE(0x1010)) + v26:ClassSubclass[Module@0x1010] = Const Value(VALUE(0x1010)) CheckInterrupts - Return v25 + Return v26 "); } @@ -4454,7 +4454,7 @@ mod hir_opt_tests { v46:ObjectSubclass[class_exact:C] = ObjectAllocClass C:VALUE(0x1008) PatchPoint NoSingletonClass(C@0x1008) PatchPoint MethodRedefined(C@0x1008, initialize@0x1038, cme:0x1040) - v50:NilClass = Const Value(nil) + v51:NilClass = Const Value(nil) CheckInterrupts Return v46 "); @@ -4491,7 +4491,7 @@ mod hir_opt_tests { v49:ObjectSubclass[class_exact:C] = ObjectAllocClass C:VALUE(0x1008) PatchPoint NoSingletonClass(C@0x1008) PatchPoint MethodRedefined(C@0x1008, initialize@0x1038, cme:0x1040) - v52:BasicObject = SendDirect v49, 0x1068, :initialize (0x1078), v15 + v53:BasicObject = SendDirect v49, 0x1068, :initialize (0x1078), v15 CheckInterrupts Return v49 "); @@ -4522,7 +4522,7 @@ mod hir_opt_tests { v46:ObjectExact = ObjectAllocClass Object:VALUE(0x1008) PatchPoint NoSingletonClass(Object@0x1008) PatchPoint MethodRedefined(Object@0x1008, initialize@0x1038, cme:0x1040) - v50:NilClass = Const Value(nil) + v51:NilClass = Const Value(nil) CheckInterrupts Return v46 "); @@ -4553,7 +4553,7 @@ mod hir_opt_tests { v46:BasicObjectExact = ObjectAllocClass BasicObject:VALUE(0x1008) PatchPoint NoSingletonClass(BasicObject@0x1008) PatchPoint MethodRedefined(BasicObject@0x1008, initialize@0x1038, cme:0x1040) - v50:NilClass = Const Value(nil) + v51:NilClass = Const Value(nil) CheckInterrupts Return v46 "); @@ -4585,7 +4585,7 @@ mod hir_opt_tests { v47:Fixnum[0] = Const Value(0) PatchPoint NoSingletonClass(Hash@0x1008) PatchPoint MethodRedefined(Hash@0x1008, initialize@0x1038, cme:0x1040) - v51:BasicObject = SendDirect v46, 0x1068, :initialize (0x1078), v47 + v52:BasicObject = SendDirect v46, 0x1068, :initialize (0x1078), v47 CheckInterrupts Return v46 "); @@ -4616,9 +4616,9 @@ mod hir_opt_tests { v15:Fixnum[1] = Const Value(1) PatchPoint MethodRedefined(Array@0x1008, new@0x1009, cme:0x1010) PatchPoint MethodRedefined(Class@0x1038, new@0x1009, cme:0x1010) - v52:BasicObject = CCallVariadic v46, :Array.new@0x1040, v15 + v53:BasicObject = CCallVariadic v46, :Array.new@0x1040, v15 CheckInterrupts - Return v52 + Return v53 "); } @@ -4677,9 +4677,9 @@ mod hir_opt_tests { v12:NilClass = Const Value(nil) PatchPoint MethodRedefined(String@0x1008, new@0x1009, cme:0x1010) PatchPoint MethodRedefined(Class@0x1038, new@0x1009, cme:0x1010) - v53:BasicObject = CCallVariadic v43, :String.new@0x1040 + v54:BasicObject = CCallVariadic v43, :String.new@0x1040 CheckInterrupts - Return v53 + Return v54 "); } @@ -4710,7 +4710,7 @@ mod hir_opt_tests { v50:RegexpExact = ObjectAllocClass Regexp:VALUE(0x1008) PatchPoint NoSingletonClass(Regexp@0x1008) PatchPoint MethodRedefined(Regexp@0x1008, initialize@0x1048, cme:0x1050) - v54:BasicObject = CCallVariadic v50, :Regexp#initialize@0x1078, v16 + v55:BasicObject = CCallVariadic v50, :Regexp#initialize@0x1078, v16 CheckInterrupts Return v50 "); @@ -4738,9 +4738,9 @@ mod hir_opt_tests { PatchPoint StableConstantNames(0x1000, C) v20:ClassSubclass[C@0x1008] = Const Value(VALUE(0x1008)) PatchPoint MethodRedefined(Class@0x1010, allocate@0x1018, cme:0x1020) - v23:ObjectSubclass[class_exact:C] = ObjectAllocClass C:VALUE(0x1008) + v24:ObjectSubclass[class_exact:C] = ObjectAllocClass C:VALUE(0x1008) CheckInterrupts - Return v23 + Return v24 "); } @@ -4798,9 +4798,9 @@ mod hir_opt_tests { PatchPoint StableConstantNames(0x1000, SC) v20:ClassSubclass[Class@0x1008] = Const Value(VALUE(0x1008)) PatchPoint MethodRedefined(Class@0x1010, allocate@0x1018, cme:0x1020) - v23:BasicObject = CCallWithFrame v20, :Class.allocate@0x1048 + v24:BasicObject = CCallWithFrame v20, :Class.allocate@0x1048 CheckInterrupts - Return v23 + Return v24 "); } @@ -6367,8 +6367,8 @@ mod hir_opt_tests { v28:ArrayExact = GuardType v10, ArrayExact PatchPoint NoSingletonClass(Array@0x1010) PatchPoint MethodRedefined(Array@0x1010, to_s@0x1018, cme:0x1020) - v33:BasicObject = CCallWithFrame v28, :Array#to_s@0x1048 - v20:String = AnyToString v28, str: v33 + v34:BasicObject = CCallWithFrame v28, :Array#to_s@0x1048 + v20:String = AnyToString v28, str: v34 v22:StringExact = StringConcat v14, v20 CheckInterrupts Return v22 @@ -6458,12 +6458,12 @@ mod hir_opt_tests { v12:Fixnum[0] = Const Value(0) PatchPoint NoSingletonClass(Array@0x1010) PatchPoint MethodRedefined(Array@0x1010, []@0x1018, cme:0x1020) - v34:CInt64[0] = Const CInt64(0) - v28:CInt64 = ArrayLength v23 - v29:CInt64[0] = GuardLess v34, v28 - v33:BasicObject = ArrayAref v23, v29 + v35:CInt64[0] = Const CInt64(0) + v29:CInt64 = ArrayLength v23 + v30:CInt64[0] = GuardLess v35, v29 + v34:BasicObject = ArrayAref v23, v30 CheckInterrupts - Return v33 + Return v34 "); // TODO(max): Check the result of `S[0] = 5; test` using `inspect` to make sure that we // actually do the load at run-time. @@ -8443,9 +8443,9 @@ mod hir_opt_tests { v11:ArrayExact = ArrayDup v10 PatchPoint NoSingletonClass(Array@0x1008) PatchPoint MethodRedefined(Array@0x1008, map@0x1010, cme:0x1018) - v21:BasicObject = SendDirect v11, 0x1040, :map (0x1050) + v22:BasicObject = SendDirect v11, 0x1040, :map (0x1050) CheckInterrupts - Return v21 + Return v22 "); } @@ -8484,7 +8484,7 @@ mod hir_opt_tests { v41:ArrayExact[VALUE(0x1018)] = Const Value(VALUE(0x1018)) PatchPoint NoSingletonClass(Array@0x1020) PatchPoint MethodRedefined(Array@0x1020, zip@0x1028, cme:0x1030) - v45:BasicObject = CCallVariadic v38, :Array#zip@0x1058, v41 + v46:BasicObject = CCallVariadic v38, :Array#zip@0x1058, v41 PatchPoint NoEPEscape(test) v24:CPtr = LoadSP v25:BasicObject = LoadField v24, :result@0x1060 @@ -8733,11 +8733,11 @@ mod hir_opt_tests { v20:ObjectSubclass[VALUE(0x1008)] = Const Value(VALUE(0x1008)) PatchPoint NoSingletonClass(C@0x1010) PatchPoint MethodRedefined(C@0x1010, foo@0x1018, cme:0x1020) - v25:CShape = LoadField v20, :shape_id@0x1048 - v26:CShape[0x1049] = GuardBitEquals v25, CShape(0x1049) - v27:NilClass = Const Value(nil) + v26:CShape = LoadField v20, :shape_id@0x1048 + v27:CShape[0x1049] = GuardBitEquals v26, CShape(0x1049) + v28:NilClass = Const Value(nil) CheckInterrupts - Return v27 + Return v28 "); } @@ -8769,11 +8769,11 @@ mod hir_opt_tests { v20:ObjectSubclass[VALUE(0x1008)] = Const Value(VALUE(0x1008)) PatchPoint NoSingletonClass(C@0x1010) PatchPoint MethodRedefined(C@0x1010, foo@0x1018, cme:0x1020) - v25:CShape = LoadField v20, :shape_id@0x1048 - v26:CShape[0x1049] = GuardBitEquals v25, CShape(0x1049) - v27:NilClass = Const Value(nil) + v26:CShape = LoadField v20, :shape_id@0x1048 + v27:CShape[0x1049] = GuardBitEquals v26, CShape(0x1049) + v28:NilClass = Const Value(nil) CheckInterrupts - Return v27 + Return v28 "); } @@ -9336,8 +9336,8 @@ mod hir_opt_tests { v14:StringExact[VALUE(0x1008)] = Const Value(VALUE(0x1008)) v28:Fixnum = GuardType v10, Fixnum PatchPoint MethodRedefined(Integer@0x1010, to_s@0x1018, cme:0x1020) - v32:StringExact = CCallVariadic v28, :Integer#to_s@0x1048 - v22:StringExact = StringConcat v14, v32 + v33:StringExact = CCallVariadic v28, :Integer#to_s@0x1048 + v22:StringExact = StringConcat v14, v33 CheckInterrupts Return v22 "); @@ -9582,9 +9582,9 @@ mod hir_opt_tests { v12:StaticSymbol[:a] = Const Value(VALUE(0x1010)) PatchPoint NoSingletonClass(Hash@0x1018) PatchPoint MethodRedefined(Hash@0x1018, []@0x1020, cme:0x1028) - v27:BasicObject = HashAref v23, v12 + v28:BasicObject = HashAref v23, v12 CheckInterrupts - Return v27 + Return v28 "); } @@ -9713,11 +9713,11 @@ mod hir_opt_tests { PatchPoint StableConstantNames(0x1000, Thread) v20:ClassSubclass[Thread@0x1008] = Const Value(VALUE(0x1008)) PatchPoint MethodRedefined(Class@0x1010, current@0x1018, cme:0x1020) - v23:CPtr = LoadEC - v24:CPtr = LoadField v23, :thread_ptr@0x1048 - v25:BasicObject = LoadField v24, :self@0x1049 + v24:CPtr = LoadEC + v25:CPtr = LoadField v24, :thread_ptr@0x1048 + v26:BasicObject = LoadField v25, :self@0x1049 CheckInterrupts - Return v25 + Return v26 "); } @@ -12402,9 +12402,9 @@ mod hir_opt_tests { v14:StringExact[VALUE(0x1000)] = Const Value(VALUE(0x1000)) PatchPoint NoSingletonClass(String@0x1008) PatchPoint MethodRedefined(String@0x1008, ==@0x1010, cme:0x1018) - v31:TrueClass = Const Value(true) + v32:TrueClass = Const Value(true) CheckInterrupts - Return v31 + Return v32 "); } @@ -12434,9 +12434,9 @@ mod hir_opt_tests { v14:StringExact[VALUE(0x1008)] = Const Value(VALUE(0x1008)) PatchPoint NoSingletonClass(String@0x1010) PatchPoint MethodRedefined(String@0x1010, ==@0x1018, cme:0x1020) - v27:FalseClass = Const Value(false) + v28:FalseClass = Const Value(false) CheckInterrupts - Return v27 + Return v28 "); } @@ -12467,9 +12467,9 @@ mod hir_opt_tests { v14:StringExact = StringCopy v13 PatchPoint NoSingletonClass(String@0x1008) PatchPoint MethodRedefined(String@0x1008, ==@0x1010, cme:0x1018) - v26:BoolExact = StringEqual v11, v14 + v27:BoolExact = StringEqual v11, v14 CheckInterrupts - Return v26 + Return v27 "); } @@ -12500,9 +12500,9 @@ mod hir_opt_tests { v14:StringExact = StringCopy v13 PatchPoint NoSingletonClass(String@0x1010) PatchPoint MethodRedefined(String@0x1010, ==@0x1018, cme:0x1020) - v26:BoolExact = StringEqual v11, v14 + v27:BoolExact = StringEqual v11, v14 CheckInterrupts - Return v26 + Return v27 "); } @@ -12532,9 +12532,9 @@ mod hir_opt_tests { v12:StringExact[VALUE(0x1000)] = Const Value(VALUE(0x1000)) PatchPoint NoSingletonClass(String@0x1008) PatchPoint MethodRedefined(String@0x1008, ==@0x1010, cme:0x1018) - v25:TrueClass = Const Value(true) + v26:TrueClass = Const Value(true) CheckInterrupts - Return v25 + Return v26 "); } @@ -12564,9 +12564,9 @@ mod hir_opt_tests { v12:StringExact[VALUE(0x1008)] = Const Value(VALUE(0x1008)) PatchPoint NoSingletonClass(String@0x1010) PatchPoint MethodRedefined(String@0x1010, ==@0x1018, cme:0x1020) - v25:FalseClass = Const Value(false) + v26:FalseClass = Const Value(false) CheckInterrupts - Return v25 + Return v26 "); } @@ -12606,13 +12606,13 @@ mod hir_opt_tests { v28:StringExact = StringCopy v27 PatchPoint NoSingletonClass(String@0x1008) PatchPoint MethodRedefined(String@0x1008, <<@0x1010, cme:0x1018) - v49:StringExact = StringAppend v17, v28 + v50:StringExact = StringAppend v17, v28 PatchPoint NoEPEscape(test) PatchPoint NoSingletonClass(String@0x1008) PatchPoint MethodRedefined(String@0x1008, ==@0x1040, cme:0x1048) - v53:BoolExact = StringEqual v17, v22 + v55:BoolExact = StringEqual v17, v22 CheckInterrupts - Return v53 + Return v55 "); } @@ -12674,10 +12674,10 @@ mod hir_opt_tests { v15:StringExact = StringCopy v14 PatchPoint NoSingletonClass(String@0x1010) PatchPoint MethodRedefined(String@0x1010, ==@0x1018, cme:0x1020) - v28:String = GuardType v10, String - v29:BoolExact = StringEqual v15, v28 + v29:String = GuardType v10, String + v30:BoolExact = StringEqual v15, v29 CheckInterrupts - Return v29 + Return v30 "); } @@ -12971,9 +12971,9 @@ mod hir_opt_tests { v27:ClassSubclass[String@0x1010] = Const Value(VALUE(0x1010)) PatchPoint NoEPEscape(test) PatchPoint MethodRedefined(Class@0x1018, ===@0x1020, cme:0x1028) - v30:BoolExact = IsA v10, v27 + v31:BoolExact = IsA v10, v27 CheckInterrupts - Return v30 + Return v31 "); } @@ -13002,9 +13002,9 @@ mod hir_opt_tests { v27:ModuleSubclass[Kernel@0x1010] = Const Value(VALUE(0x1010)) PatchPoint NoEPEscape(test) PatchPoint MethodRedefined(Module@0x1018, ===@0x1020, cme:0x1028) - v30:BoolExact = CCall v27, :Module#===@0x1050, v10 + v31:BoolExact = CCall v27, :Module#===@0x1050, v10 CheckInterrupts - Return v30 + Return v31 "); } @@ -13262,9 +13262,9 @@ mod hir_opt_tests { PatchPoint StableConstantNames(0x1000, Integer) v22:ClassSubclass[Integer@0x1008] = Const Value(VALUE(0x1008)) PatchPoint MethodRedefined(Integer@0x1008, is_a?@0x1009, cme:0x1010) - v26:TrueClass = Const Value(true) + v27:TrueClass = Const Value(true) CheckInterrupts - Return v26 + Return v27 "); } @@ -13290,9 +13290,9 @@ mod hir_opt_tests { PatchPoint StableConstantNames(0x1000, String) v22:ClassSubclass[String@0x1008] = Const Value(VALUE(0x1008)) PatchPoint MethodRedefined(Integer@0x1010, is_a?@0x1018, cme:0x1020) - v26:FalseClass = Const Value(false) + v27:FalseClass = Const Value(false) CheckInterrupts - Return v26 + Return v27 "); } @@ -13322,9 +13322,9 @@ mod hir_opt_tests { v25:ClassSubclass[Array@0x1018] = Const Value(VALUE(0x1018)) PatchPoint NoSingletonClass(C@0x1020) PatchPoint MethodRedefined(C@0x1020, is_a?@0x1028, cme:0x1030) - v30:TrueClass = Const Value(true) + v31:TrueClass = Const Value(true) CheckInterrupts - Return v30 + Return v31 "); } @@ -13354,9 +13354,9 @@ mod hir_opt_tests { v25:ClassSubclass[C@0x1018] = Const Value(VALUE(0x1018)) PatchPoint NoSingletonClass(C@0x1018) PatchPoint MethodRedefined(C@0x1018, is_a?@0x1019, cme:0x1020) - v30:TrueClass = Const Value(true) + v31:TrueClass = Const Value(true) CheckInterrupts - Return v30 + Return v31 "); } @@ -13384,9 +13384,9 @@ mod hir_opt_tests { PatchPoint StableConstantNames(0x1010, Symbol) v25:ClassSubclass[Symbol@0x1018] = Const Value(VALUE(0x1018)) PatchPoint MethodRedefined(Symbol@0x1018, is_a?@0x1019, cme:0x1020) - v29:TrueClass = Const Value(true) + v30:TrueClass = Const Value(true) CheckInterrupts - Return v29 + Return v30 "); } @@ -13510,7 +13510,7 @@ mod hir_opt_tests { v15:TrueClass = Const Value(true) PatchPoint MethodRedefined(Class@0x1040, respond_to?@0x1048, cme:0x1050) PatchPoint MethodRedefined(Class@0x1040, _lex_actions@0x1078, cme:0x1080) - v51:TrueClass = Const Value(true) + v52:TrueClass = Const Value(true) CheckInterrupts v26:StaticSymbol[:CORRECT] = Const Value(VALUE(0x10a8)) Return v26 @@ -13543,9 +13543,9 @@ mod hir_opt_tests { v25:ObjectSubclass[class_exact:C] = GuardType v10, ObjectSubclass[class_exact:C] v28:ClassSubclass[C@0x1008] = Const Value(VALUE(0x1008)) PatchPoint MethodRedefined(Class@0x1040, name@0x1048, cme:0x1050) - v31:StringExact|NilClass = CCall v28, :Module#name@0x1078 + v32:StringExact|NilClass = CCall v28, :Module#name@0x1078 CheckInterrupts - Return v31 + Return v32 "); } @@ -13598,9 +13598,9 @@ mod hir_opt_tests { bb3(v6:BasicObject): v10:Fixnum[5] = Const Value(5) PatchPoint MethodRedefined(Integer@0x1000, class@0x1008, cme:0x1010) - v21:ClassSubclass[Integer@0x1000] = Const Value(VALUE(0x1000)) + v22:ClassSubclass[Integer@0x1000] = Const Value(VALUE(0x1000)) CheckInterrupts - Return v21 + Return v22 "); } @@ -13653,9 +13653,9 @@ mod hir_opt_tests { v12:Fixnum[0] = Const Value(0) PatchPoint NoSingletonClass(Array@0x1010) PatchPoint MethodRedefined(Array@0x1010, []@0x1018, cme:0x1020) - v36:ModuleExact[VALUE(0x1048)] = Const Value(VALUE(0x1048)) + v37:ModuleExact[VALUE(0x1048)] = Const Value(VALUE(0x1048)) CheckInterrupts - Return v36 + Return v37 "); } @@ -13704,14 +13704,14 @@ mod hir_opt_tests { SetIvar v15, :@formatted, v16 v47:ClassSubclass[VMFrozenCore] = Const Value(VALUE(0x1008)) PatchPoint MethodRedefined(Class@0x1010, lambda@0x1018, cme:0x1020) - v62:BasicObject = CCallWithFrame v47, :RubyVM::FrozenCore.lambda@0x1048, block=0x1050 + v63:BasicObject = CCallWithFrame v47, :RubyVM::FrozenCore.lambda@0x1048, block=0x1050 v50:CPtr = GetEP 0 v51:BasicObject = LoadField v50, :a@0x1001 v52:BasicObject = LoadField v50, :_b@0x1002 v53:BasicObject = LoadField v50, :_c@0x1058 v54:BasicObject = LoadField v50, :formatted@0x1059 CheckInterrupts - Return v62 + Return v63 "); } @@ -13748,9 +13748,9 @@ mod hir_opt_tests { v20:ObjectSubclass[VALUE(0x1008)] = Const Value(VALUE(0x1008)) PatchPoint NoSingletonClass(TestFrozen@0x1010) PatchPoint MethodRedefined(TestFrozen@0x1010, a@0x1018, cme:0x1020) - v29:Fixnum[1] = Const Value(1) + v30:Fixnum[1] = Const Value(1) CheckInterrupts - Return v29 + Return v30 "); } @@ -13789,9 +13789,9 @@ mod hir_opt_tests { v20:ObjectSubclass[VALUE(0x1008)] = Const Value(VALUE(0x1008)) PatchPoint NoSingletonClass(TestMultiIvars@0x1010) PatchPoint MethodRedefined(TestMultiIvars@0x1010, b@0x1018, cme:0x1020) - v29:Fixnum[20] = Const Value(20) + v30:Fixnum[20] = Const Value(20) CheckInterrupts - Return v29 + Return v30 "); } @@ -13828,9 +13828,9 @@ mod hir_opt_tests { v20:ObjectSubclass[VALUE(0x1008)] = Const Value(VALUE(0x1008)) PatchPoint NoSingletonClass(TestFrozenStr@0x1010) PatchPoint MethodRedefined(TestFrozenStr@0x1010, name@0x1018, cme:0x1020) - v29:StringExact[VALUE(0x1048)] = Const Value(VALUE(0x1048)) + v30:StringExact[VALUE(0x1048)] = Const Value(VALUE(0x1048)) CheckInterrupts - Return v29 + Return v30 "); } @@ -13867,9 +13867,9 @@ mod hir_opt_tests { v20:ObjectSubclass[VALUE(0x1008)] = Const Value(VALUE(0x1008)) PatchPoint NoSingletonClass(TestFrozenNil@0x1010) PatchPoint MethodRedefined(TestFrozenNil@0x1010, value@0x1018, cme:0x1020) - v29:NilClass = Const Value(nil) + v30:NilClass = Const Value(nil) CheckInterrupts - Return v29 + Return v30 "); } @@ -13906,11 +13906,11 @@ mod hir_opt_tests { v20:ObjectSubclass[VALUE(0x1008)] = Const Value(VALUE(0x1008)) PatchPoint NoSingletonClass(TestUnfrozen@0x1010) PatchPoint MethodRedefined(TestUnfrozen@0x1010, a@0x1018, cme:0x1020) - v25:CShape = LoadField v20, :shape_id@0x1048 - v26:CShape[0x1049] = GuardBitEquals v25, CShape(0x1049) - v27:BasicObject = LoadField v20, :@a@0x104a + v26:CShape = LoadField v20, :shape_id@0x1048 + v27:CShape[0x1049] = GuardBitEquals v26, CShape(0x1049) + v28:BasicObject = LoadField v20, :@a@0x104a CheckInterrupts - Return v27 + Return v28 "); } @@ -13947,9 +13947,9 @@ mod hir_opt_tests { v20:ObjectSubclass[VALUE(0x1008)] = Const Value(VALUE(0x1008)) PatchPoint NoSingletonClass(TestAttrReader@0x1010) PatchPoint MethodRedefined(TestAttrReader@0x1010, value@0x1018, cme:0x1020) - v29:Fixnum[42] = Const Value(42) + v30:Fixnum[42] = Const Value(42) CheckInterrupts - Return v29 + Return v30 "); } @@ -13986,9 +13986,9 @@ mod hir_opt_tests { v20:ObjectSubclass[VALUE(0x1008)] = Const Value(VALUE(0x1008)) PatchPoint NoSingletonClass(TestFrozenSym@0x1010) PatchPoint MethodRedefined(TestFrozenSym@0x1010, sym@0x1018, cme:0x1020) - v29:StaticSymbol[:hello] = Const Value(VALUE(0x1048)) + v30:StaticSymbol[:hello] = Const Value(VALUE(0x1048)) CheckInterrupts - Return v29 + Return v30 "); } @@ -14025,9 +14025,9 @@ mod hir_opt_tests { v20:ObjectSubclass[VALUE(0x1008)] = Const Value(VALUE(0x1008)) PatchPoint NoSingletonClass(TestFrozenBool@0x1010) PatchPoint MethodRedefined(TestFrozenBool@0x1010, flag@0x1018, cme:0x1020) - v29:TrueClass = Const Value(true) + v30:TrueClass = Const Value(true) CheckInterrupts - Return v29 + Return v30 "); } @@ -14106,15 +14106,15 @@ mod hir_opt_tests { v27:ObjectSubclass[VALUE(0x1008)] = Const Value(VALUE(0x1008)) PatchPoint NoSingletonClass(TestNestedAccess@0x1010) PatchPoint MethodRedefined(TestNestedAccess@0x1010, x@0x1018, cme:0x1020) - v51:Fixnum[100] = Const Value(100) + v53:Fixnum[100] = Const Value(100) PatchPoint StableConstantNames(0x1048, NESTED_FROZEN) - v33:ObjectSubclass[VALUE(0x1008)] = Const Value(VALUE(0x1008)) + v34:ObjectSubclass[VALUE(0x1008)] = Const Value(VALUE(0x1008)) PatchPoint MethodRedefined(TestNestedAccess@0x1010, y@0x1050, cme:0x1058) - v53:Fixnum[200] = Const Value(200) + v55:Fixnum[200] = Const Value(200) PatchPoint MethodRedefined(Integer@0x1080, +@0x1088, cme:0x1090) - v54:Fixnum[300] = Const Value(300) + v56:Fixnum[300] = Const Value(300) CheckInterrupts - Return v54 + Return v56 "); } @@ -14141,10 +14141,10 @@ mod hir_opt_tests { v20:StringExact[VALUE(0x1008)] = Const Value(VALUE(0x1008)) PatchPoint NoSingletonClass(String@0x1010) PatchPoint MethodRedefined(String@0x1010, bytesize@0x1018, cme:0x1020) - v24:CInt64 = LoadField v20, :len@0x1048 - v25:Fixnum = BoxFixnum v24 + v25:CInt64 = LoadField v20, :len@0x1048 + v26:Fixnum = BoxFixnum v25 CheckInterrupts - Return v25 + Return v26 "); } @@ -15606,7 +15606,7 @@ mod hir_opt_tests { v26:Fixnum[5] = Const Value(5) PatchPoint NoEPEscape(initialize) PatchPoint MethodRedefined(Integer@0x1008, +@0x1010, cme:0x1018) - v64:Fixnum[6] = Const Value(6) + v65:Fixnum[6] = Const Value(6) PatchPoint SingleRactorMode WriteBarrier v23, v16 CheckInterrupts @@ -15914,9 +15914,9 @@ mod hir_opt_tests { v21:Fixnum[2] = Const Value(2) v40:Fixnum = FixnumSub v35, v21 PatchPoint MethodRedefined(Integer@0x1008, +@0x1040, cme:0x1048) - v43:Fixnum = FixnumAdd v36, v40 + v44:Fixnum = FixnumAdd v36, v40 CheckInterrupts - Return v43 + Return v44 "); } @@ -16528,20 +16528,20 @@ mod hir_opt_tests { v138:ObjectSubclass[class_exact:C] = GuardType v12, ObjectSubclass[class_exact:C] v139:BasicObject = LoadField v138, :var@0x1040 PatchPoint MethodRedefined(Integer@0x1048, +@0x1050, cme:0x1058) - v178:Fixnum = GuardType v139, Fixnum - v179:Fixnum = FixnumAdd v17, v178 + v179:Fixnum = GuardType v139, Fixnum + v180:Fixnum = FixnumAdd v17, v179 PatchPoint NoEPEscape(test) - v183:Fixnum = FixnumAdd v179, v178 - v187:Fixnum = FixnumAdd v183, v178 - v191:Fixnum = FixnumAdd v187, v178 - v195:Fixnum = FixnumAdd v191, v178 - v199:Fixnum = FixnumAdd v195, v178 - v203:Fixnum = FixnumAdd v199, v178 - v207:Fixnum = FixnumAdd v203, v178 - v211:Fixnum = FixnumAdd v207, v178 - v215:Fixnum = FixnumAdd v211, v178 + v185:Fixnum = FixnumAdd v180, v179 + v190:Fixnum = FixnumAdd v185, v179 + v195:Fixnum = FixnumAdd v190, v179 + v200:Fixnum = FixnumAdd v195, v179 + v205:Fixnum = FixnumAdd v200, v179 + v210:Fixnum = FixnumAdd v205, v179 + v215:Fixnum = FixnumAdd v210, v179 + v220:Fixnum = FixnumAdd v215, v179 + v225:Fixnum = FixnumAdd v220, v179 CheckInterrupts - Return v215 + Return v225 "); } @@ -16568,10 +16568,10 @@ mod hir_opt_tests { v21:ArrayExact[VALUE(0x1008)] = Const Value(VALUE(0x1008)) PatchPoint NoSingletonClass(Array@0x1010) PatchPoint MethodRedefined(Array@0x1010, length@0x1018, cme:0x1020) - v25:CInt64 = ArrayLength v21 - v26:Fixnum = BoxFixnum v25 + v26:CInt64 = ArrayLength v21 + v27:Fixnum = BoxFixnum v26 CheckInterrupts - Return v26 + Return v27 "); } @@ -16598,10 +16598,10 @@ mod hir_opt_tests { v21:ArrayExact[VALUE(0x1008)] = Const Value(VALUE(0x1008)) PatchPoint NoSingletonClass(Array@0x1010) PatchPoint MethodRedefined(Array@0x1010, length@0x1018, cme:0x1020) - v27:CInt64[4] = Const CInt64(4) - v26:Fixnum = BoxFixnum v27 + v28:CInt64[4] = Const CInt64(4) + v27:Fixnum = BoxFixnum v28 CheckInterrupts - Return v26 + Return v27 "); } } diff --git a/zjit/src/hir/tests.rs b/zjit/src/hir/tests.rs index 00c6f70550d2d9..7a1cd85c5fb38f 100644 --- a/zjit/src/hir/tests.rs +++ b/zjit/src/hir/tests.rs @@ -50,10 +50,10 @@ mod snapshot_tests { v12:Fixnum[2] = Const Value(2) v13:Any = Snapshot FrameState { pc: 0x1008, stack: [v10, v12], locals: [] } PatchPoint MethodRedefined(Integer@0x1010, +@0x1018, cme:0x1020) - v33:Fixnum[6] = Const Value(6) - v21:Any = Snapshot FrameState { pc: 0x1048, stack: [v33], locals: [] } + v35:Fixnum[6] = Const Value(6) + v21:Any = Snapshot FrameState { pc: 0x1048, stack: [v35], locals: [] } CheckInterrupts - Return v33 + Return v35 "); } From 3b5d49449dfe33df2bd0a5a3285b201060c7213f Mon Sep 17 00:00:00 2001 From: Aaron Patterson Date: Thu, 21 May 2026 14:02:17 -0700 Subject: [PATCH 07/11] ZJIT: Fix exit tracing so that recompiles still happen (#17061) * ZJIT: Fix exit tracing so that recompiles still happen Before this commit, exit tracing would not allow recompiles to happen. Exit tracing works by installing custom exit handlers in order to trace exit locations. Recompilation also works by installing custom exit handlers. Exit tracing didn't take in to account that the particular exit might want to recompile, so recompiles weren't happening when exit tracing was enabled. We can see the consequences with the following code: ```ruby class C def initialize(extra) @attribute_types = nil @a = 1 if extra >= 1 @b = 1 if extra >= 2 end def attribute_types @attribute_types ||= 123 end end a = C.new(1) b = C.new(2) c = C.new(0) 5_000.times { a.attribute_types } 50_000.times do b.attribute_types c.attribute_types end ``` The `attribute_types` method is initially compiled as a monomorphic ivar read. We expect recompilation to occur and it _should_ get recompiled as a polymorphic ivar read. Before this patch, if you run miniruby like this: ``` ./miniruby --zjit --zjit-trace-compiles --zjit-trace-exits --zjit-trace-invalidation --zjit-dump-hir --zjit-max-versions=10 test.rb ``` The output HIR will be like this (I've omitted a bunch of BBs to make it readable): ``` Optimized HIR: fn attribute_types@../test.rb:9: bb3(v6:BasicObject): PatchPoint SingleRactorMode v36:HeapBasicObject = GuardType v6, HeapBasicObject v37:CShape = LoadField v36, :shape_id@0x4 v38:CShape[0x180009] = GuardBitEquals v37, CShape(0x180009) v39:BasicObject = LoadField v36, :@attribute_types@0x10 CheckInterrupts v15:CBool = Test v39 v16:Truthy = RefineType v39, Truthy CondBranch v15, bb4(v36, v16), bb5() Optimized HIR: fn attribute_types@../test.rb:9: bb3(v6:BasicObject): PatchPoint SingleRactorMode v36:HeapBasicObject = GuardType v6, HeapBasicObject v37:CShape = LoadField v36, :shape_id@0x4 v38:CShape[0x180009] = GuardBitEquals v37, CShape(0x180009) v39:BasicObject = LoadField v36, :@attribute_types@0x10 CheckInterrupts v15:CBool = Test v39 v16:Truthy = RefineType v39, Truthy CondBranch v15, bb4(v36, v16), bb5() ``` Note that the `attribute_types` method stays monomorphic. After this patch, the HIR looks like this: ``` Optimized HIR: fn attribute_types@../test.rb:9: bb3(v6:BasicObject): PatchPoint SingleRactorMode v36:HeapBasicObject = GuardType v6, HeapBasicObject v37:CShape = LoadField v36, :shape_id@0x4 v38:CShape[0x180009] = GuardBitEquals v37, CShape(0x180009) recompile v39:BasicObject = LoadField v36, :@attribute_types@0x10 CheckInterrupts v15:CBool = Test v39 v16:Truthy = RefineType v39, Truthy CondBranch v15, bb4(v36, v16), bb5() Optimized HIR: fn attribute_types@../test.rb:9: bb3(v6:BasicObject): PatchPoint SingleRactorMode v11:HeapBasicObject = GuardType v6, HeapBasicObject v12:CUInt64 = LoadField v11, :RBASIC_FLAGS@0x0 v14:CUInt64[0xffffffff0000001f] = Const CUInt64(0xffffffff0000001f) v15:CPtr[CPtr(0x18000900000001)] = Const CPtr(0x18000900000001) v16 = RefineType v15, CUInt64 v17:CInt64 = IntAnd v12, v14 v18:CBool = IsBitEqual v17, v16 CondBranch v18, bb6(), bb7() bb7(): v22:CUInt64[0xffffffff0000001f] = Const CUInt64(0xffffffff0000001f) v23:CPtr[CPtr(0x18000a00000001)] = Const CPtr(0x18000a00000001) v24 = RefineType v23, CUInt64 v25:CInt64 = IntAnd v12, v22 v26:CBool = IsBitEqual v25, v24 CondBranch v26, bb8(), bb9() bb9(): v30:CUInt64[0xffffffff0000001f] = Const CUInt64(0xffffffff0000001f) v31:CPtr[CPtr(0x18000800000001)] = Const CPtr(0x18000800000001) v32 = RefineType v31, CUInt64 v33:CInt64 = IntAnd v12, v30 v34:CBool = IsBitEqual v33, v32 CondBranch v34, bb10(), bb11() ``` You can see it's polymorphic now * Pull compile_exit in to compile_exit_body This way we can share implementation between normal exit compilation and when we need to record exits * update comment --- zjit/src/backend/lir.rs | 64 +++++++++++++++++++++------------------ zjit/src/hir.rs | 8 ++++- zjit/src/hir/opt_tests.rs | 44 +++++++++++++-------------- 3 files changed, 64 insertions(+), 52 deletions(-) diff --git a/zjit/src/backend/lir.rs b/zjit/src/backend/lir.rs index 467cd5b4de5624..975c46529a2af8 100644 --- a/zjit/src/backend/lir.rs +++ b/zjit/src/backend/lir.rs @@ -2661,24 +2661,8 @@ impl Assembler asm.cret(Opnd::UImm(Qundef.as_u64())); } - /// Compile the main side-exit code. This function takes only SideExit so - /// that it can be safely deduplicated by using SideExit as a dedup key. - fn compile_exit(asm: &mut Assembler, exit: &SideExit) { - compile_exit_save_state(asm, exit); - // If this side exit should trigger recompilation, call the recompile - // function after saving VM state. The ccall must happen after - // compile_exit_save_state because it clobbers caller-saved registers - // that may hold stack/local operands we need to save. + fn compile_exit_recompile(asm: &mut Assembler, exit: &SideExit) { if let Some(recompile) = &exit.recompile { - if cfg!(feature = "runtime_checks") { - // Clear jit_return to fully materialize the frame. This must happen - // before any C call in the exit path (e.g. exit_recompile) - // because that C call can trigger GC, which walks the stack and would - // hit the CFP_JIT_RETURN assertion if jit_return still holds the - // runtime_checks poison value (JIT_RETURN_POISON). - asm_comment!(asm, "clear cfp->jit_return"); - asm.store(Opnd::mem(64, CFP, RUBY_OFFSET_CFP_JIT_RETURN), 0.into()); - } use crate::codegen::exit_recompile; asm_comment!(asm, "profile and maybe recompile"); @@ -2692,6 +2676,38 @@ impl Assembler }) ); } + } + + /// Compile the main side-exit code. The side exit will optionally record a traced exit + /// stack, optionally trigger recompilation, and then return to the interpreter. Shared + /// exits pass no trace reason so they can still be deduplicated by SideExit. + /// IOW, we should never pass a trace reason if we expect the exit to be + /// deduplicated. + fn compile_exit(asm: &mut Assembler, exit: &SideExit, trace_reason: Option) { + // Save VM state before the ccall so that + // rb_profile_frames sees valid cfp->pc and the + // ccall doesn't clobber caller-saved registers + // holding stack/local operands. + compile_exit_save_state(asm, exit); + if trace_reason.is_some() || exit.recompile.is_some() { + if cfg!(feature = "runtime_checks") { + // Clear jit_return to fully materialize the frame. This must happen + // before any C call in the exit path because that C call can trigger + // GC, which walks the stack and would hit the CFP_JIT_RETURN assertion + // if jit_return still holds the runtime_checks poison value + // (JIT_RETURN_POISON). + asm_comment!(asm, "clear cfp->jit_return"); + asm.store(Opnd::mem(64, CFP, RUBY_OFFSET_CFP_JIT_RETURN), 0.into()); + } + } + if let Some(reason) = trace_reason { + // Leak a CString with the reason so it's available at runtime + let reason_cstr = std::ffi::CString::new(reason.to_string()) + .unwrap_or_else(|_| std::ffi::CString::new("unknown").unwrap()); + let reason_ptr = reason_cstr.into_raw() as *const u8; + asm_ccall!(asm, rb_zjit_record_exit_stack, Opnd::const_ptr(reason_ptr)); + } + compile_exit_recompile(asm, exit); compile_exit_return(asm); } @@ -2768,17 +2784,7 @@ impl Assembler } if should_record_exit { - // Save VM state before the ccall so that - // rb_profile_frames sees valid cfp->pc and the - // ccall doesn't clobber caller-saved registers - // holding stack/local operands. - compile_exit_save_state(self, &exit); - // Leak a CString with the reason so it's available at runtime - let reason_cstr = std::ffi::CString::new(reason.to_string()) - .unwrap_or_else(|_| std::ffi::CString::new("unknown").unwrap()); - let reason_ptr = reason_cstr.into_raw() as *const u8; - asm_ccall!(self, rb_zjit_record_exit_stack, Opnd::const_ptr(reason_ptr)); - compile_exit_return(self); + compile_exit(self, &exit, Some(reason)); } else { // If the side exit has already been compiled, jump to it. // Otherwise, let it fall through and compile the exit next. @@ -2798,7 +2804,7 @@ impl Assembler let new_exit = self.new_label("side_exit"); self.write_label(new_exit.clone()); asm_comment!(self, "Exit: {pc}"); - compile_exit(self, &exit); + compile_exit(self, &exit, None); compiled_exits.insert(exit, new_exit.unwrap_label()); new_exit }; diff --git a/zjit/src/hir.rs b/zjit/src/hir.rs index 76ae1bb37f71e2..93dbbec690ab2d 100644 --- a/zjit/src/hir.rs +++ b/zjit/src/hir.rs @@ -2099,7 +2099,13 @@ impl<'a> std::fmt::Display for InsnPrinter<'a> { Insn::GuardType { val, guard_type, .. } => { write!(f, "GuardType {val}, {}", guard_type.print(self.ptr_map)) }, Insn::RefineType { val, new_type, .. } => { write!(f, "RefineType {val}, {}", new_type.print(self.ptr_map)) }, Insn::HasType { val, expected, .. } => { write!(f, "HasType {val}, {}", expected.print(self.ptr_map)) }, - Insn::GuardBitEquals { val, expected, .. } => { write!(f, "GuardBitEquals {val}, {}", expected.print(self.ptr_map)) }, + Insn::GuardBitEquals { val, expected, recompile, .. } => { + write!(f, "GuardBitEquals {val}, {}", expected.print(self.ptr_map))?; + if recompile.is_some() { + write!(f, " recompile")?; + } + return Ok(()) + }, Insn::GuardAnyBitSet { val, mask, mask_name: Some(name), .. } => { write!(f, "GuardAnyBitSet {val}, {name}={}", mask.print(self.ptr_map)) }, Insn::GuardAnyBitSet { val, mask, .. } => { write!(f, "GuardAnyBitSet {val}, {}", mask.print(self.ptr_map)) }, Insn::GuardNoBitsSet { val, mask, mask_name: Some(name), .. } => { write!(f, "GuardNoBitsSet {val}, {name}={}", mask.print(self.ptr_map)) }, diff --git a/zjit/src/hir/opt_tests.rs b/zjit/src/hir/opt_tests.rs index 9950d9ce439929..4a83b40e38e5b9 100644 --- a/zjit/src/hir/opt_tests.rs +++ b/zjit/src/hir/opt_tests.rs @@ -5269,7 +5269,7 @@ mod hir_opt_tests { bb3(v6:BasicObject): v16:HeapBasicObject = GuardType v6, HeapBasicObject v17:CShape = LoadField v16, :shape_id@0x1000 - v18:CShape[0x1001] = GuardBitEquals v17, CShape(0x1001) + v18:CShape[0x1001] = GuardBitEquals v17, CShape(0x1001) recompile v19:StringExact[VALUE(0x1008)] = Const Value(VALUE(0x1008)) CheckInterrupts Return v19 @@ -5295,7 +5295,7 @@ mod hir_opt_tests { bb3(v6:BasicObject): v16:HeapBasicObject = GuardType v6, HeapBasicObject v17:CShape = LoadField v16, :shape_id@0x1000 - v18:CShape[0x1001] = GuardBitEquals v17, CShape(0x1001) + v18:CShape[0x1001] = GuardBitEquals v17, CShape(0x1001) recompile v19:NilClass = Const Value(nil) CheckInterrupts Return v19 @@ -5613,7 +5613,7 @@ mod hir_opt_tests { PatchPoint SingleRactorMode v21:HeapBasicObject = GuardType v6, HeapBasicObject v22:CShape = LoadField v21, :shape_id@0x1000 - v23:CShape[0x1001] = GuardBitEquals v22, CShape(0x1001) + v23:CShape[0x1001] = GuardBitEquals v22, CShape(0x1001) recompile StoreField v21, :@foo@0x1002, v10 WriteBarrier v21, v10 CheckInterrupts @@ -5642,7 +5642,7 @@ mod hir_opt_tests { PatchPoint SingleRactorMode v21:HeapBasicObject = GuardType v6, HeapBasicObject v22:CShape = LoadField v21, :shape_id@0x1000 - v23:CShape[0x1001] = GuardBitEquals v22, CShape(0x1001) + v23:CShape[0x1001] = GuardBitEquals v22, CShape(0x1001) recompile StoreField v21, :@foo@0x1002, v10 WriteBarrier v21, v10 v26:CShape[0x1003] = Const CShape(0x1003) @@ -5687,7 +5687,7 @@ mod hir_opt_tests { PatchPoint SingleRactorMode v28:HeapBasicObject = GuardType v6, HeapBasicObject v29:CShape = LoadField v28, :shape_id@0x1000 - v30:CShape[0x1001] = GuardBitEquals v29, CShape(0x1001) + v30:CShape[0x1001] = GuardBitEquals v29, CShape(0x1001) recompile StoreField v28, :@foo@0x1002, v10 WriteBarrier v28, v10 v33:CShape[0x1003] = Const CShape(0x1003) @@ -7498,7 +7498,7 @@ mod hir_opt_tests { PatchPoint MethodRedefined(C@0x1008, foo@0x1010, cme:0x1018) v23:ObjectSubclass[class_exact:C] = GuardType v10, ObjectSubclass[class_exact:C] v26:CShape = LoadField v23, :shape_id@0x1040 - v27:CShape[0x1041] = GuardBitEquals v26, CShape(0x1041) + v27:CShape[0x1041] = GuardBitEquals v26, CShape(0x1041) recompile v28:BasicObject = LoadField v23, :@foo@0x1042 CheckInterrupts Return v28 @@ -7766,7 +7766,7 @@ mod hir_opt_tests { PatchPoint SingleRactorMode v17:Module = GuardType v6, Module v18:CShape = LoadField v17, :shape_id@0x1000 - v19:CShape[0x1001] = GuardBitEquals v18, CShape(0x1001) + v19:CShape[0x1001] = GuardBitEquals v18, CShape(0x1001) recompile PatchPoint RootBoxOnly v21:RubyValue = LoadField v17, :fields_obj@0x1002 v22:BasicObject = LoadField v21, :@foo@0x1003 @@ -7838,7 +7838,7 @@ mod hir_opt_tests { PatchPoint SingleRactorMode v17:Class = GuardType v6, Class v18:CShape = LoadField v17, :shape_id@0x1000 - v19:CShape[0x1001] = GuardBitEquals v18, CShape(0x1001) + v19:CShape[0x1001] = GuardBitEquals v18, CShape(0x1001) recompile PatchPoint RootBoxOnly v21:RubyValue = LoadField v17, :fields_obj@0x1002 v22:BasicObject = LoadField v21, :@foo@0x1003 @@ -7903,7 +7903,7 @@ mod hir_opt_tests { PatchPoint SingleRactorMode v17:HeapBasicObject = GuardType v6, HeapBasicObject v18:CShape = LoadField v17, :shape_id@0x1000 - v19:CShape[0x1001] = GuardBitEquals v18, CShape(0x1001) + v19:CShape[0x1001] = GuardBitEquals v18, CShape(0x1001) recompile v20:CAttrIndex[0] = Const CAttrIndex(0) v21:BasicObject = CCall v17, :rb_ivar_get_at_no_ractor_check@0x1008, v20 CheckInterrupts @@ -7938,7 +7938,7 @@ mod hir_opt_tests { PatchPoint SingleRactorMode v17:TypedTData = GuardType v6, TypedTData v18:CShape = LoadField v17, :shape_id@0x1000 - v19:CShape[0x1001] = GuardBitEquals v18, CShape(0x1001) + v19:CShape[0x1001] = GuardBitEquals v18, CShape(0x1001) recompile v20:RubyValue = LoadField v17, :fields_obj@0x1002 v21:BasicObject = LoadField v20, :@a@0x1002 CheckInterrupts @@ -8175,7 +8175,7 @@ mod hir_opt_tests { Jump bb4(v29) bb8(): v44:CShape = LoadField v11, :shape_id@0x1005 - v45:CShape[0x1006] = GuardBitEquals v44, CShape(0x1006) + v45:CShape[0x1006] = GuardBitEquals v44, CShape(0x1006) recompile v46:CPtr = LoadField v11, :as_heap@0x1002 v47:BasicObject = LoadField v46, :@foo@0x1000 Jump bb4(v47) @@ -8734,7 +8734,7 @@ mod hir_opt_tests { PatchPoint NoSingletonClass(C@0x1010) PatchPoint MethodRedefined(C@0x1010, foo@0x1018, cme:0x1020) v26:CShape = LoadField v20, :shape_id@0x1048 - v27:CShape[0x1049] = GuardBitEquals v26, CShape(0x1049) + v27:CShape[0x1049] = GuardBitEquals v26, CShape(0x1049) recompile v28:NilClass = Const Value(nil) CheckInterrupts Return v28 @@ -8770,7 +8770,7 @@ mod hir_opt_tests { PatchPoint NoSingletonClass(C@0x1010) PatchPoint MethodRedefined(C@0x1010, foo@0x1018, cme:0x1020) v26:CShape = LoadField v20, :shape_id@0x1048 - v27:CShape[0x1049] = GuardBitEquals v26, CShape(0x1049) + v27:CShape[0x1049] = GuardBitEquals v26, CShape(0x1049) recompile v28:NilClass = Const Value(nil) CheckInterrupts Return v28 @@ -8806,7 +8806,7 @@ mod hir_opt_tests { PatchPoint MethodRedefined(C@0x1008, foo@0x1010, cme:0x1018) v23:ObjectSubclass[class_exact:C] = GuardType v10, ObjectSubclass[class_exact:C] v26:CShape = LoadField v23, :shape_id@0x1040 - v27:CShape[0x1041] = GuardBitEquals v26, CShape(0x1041) + v27:CShape[0x1041] = GuardBitEquals v26, CShape(0x1041) recompile v28:NilClass = Const Value(nil) CheckInterrupts Return v28 @@ -8842,7 +8842,7 @@ mod hir_opt_tests { PatchPoint MethodRedefined(C@0x1008, foo@0x1010, cme:0x1018) v23:ObjectSubclass[class_exact:C] = GuardType v10, ObjectSubclass[class_exact:C] v26:CShape = LoadField v23, :shape_id@0x1040 - v27:CShape[0x1041] = GuardBitEquals v26, CShape(0x1041) + v27:CShape[0x1041] = GuardBitEquals v26, CShape(0x1041) recompile v28:NilClass = Const Value(nil) CheckInterrupts Return v28 @@ -13907,7 +13907,7 @@ mod hir_opt_tests { PatchPoint NoSingletonClass(TestUnfrozen@0x1010) PatchPoint MethodRedefined(TestUnfrozen@0x1010, a@0x1018, cme:0x1020) v26:CShape = LoadField v20, :shape_id@0x1048 - v27:CShape[0x1049] = GuardBitEquals v26, CShape(0x1049) + v27:CShape[0x1049] = GuardBitEquals v26, CShape(0x1049) recompile v28:BasicObject = LoadField v20, :@a@0x104a CheckInterrupts Return v28 @@ -14065,7 +14065,7 @@ mod hir_opt_tests { PatchPoint MethodRedefined(TestDynamic@0x1008, val@0x1010, cme:0x1018) v23:ObjectSubclass[class_exact:TestDynamic] = GuardType v10, ObjectSubclass[class_exact:TestDynamic] v26:CShape = LoadField v23, :shape_id@0x1040 - v27:CShape[0x1041] = GuardBitEquals v26, CShape(0x1041) + v27:CShape[0x1041] = GuardBitEquals v26, CShape(0x1041) recompile v28:BasicObject = LoadField v23, :@val@0x1042 CheckInterrupts Return v28 @@ -15549,7 +15549,7 @@ mod hir_opt_tests { PatchPoint SingleRactorMode v35:HeapBasicObject = GuardType v8, HeapBasicObject v36:CShape = LoadField v35, :shape_id@0x1000 - v37:CShape[0x1001] = GuardBitEquals v36, CShape(0x1001) + v37:CShape[0x1001] = GuardBitEquals v36, CShape(0x1001) recompile StoreField v35, :@a@0x1002, v13 WriteBarrier v35, v13 v40:CShape[0x1003] = Const CShape(0x1003) @@ -15597,7 +15597,7 @@ mod hir_opt_tests { PatchPoint SingleRactorMode v49:HeapBasicObject = GuardType v10, HeapBasicObject v50:CShape = LoadField v49, :shape_id@0x1000 - v51:CShape[0x1001] = GuardBitEquals v50, CShape(0x1001) + v51:CShape[0x1001] = GuardBitEquals v50, CShape(0x1001) recompile StoreField v49, :@a@0x1002, v16 WriteBarrier v49, v16 v54:CShape[0x1003] = Const CShape(0x1003) @@ -15645,7 +15645,7 @@ mod hir_opt_tests { PatchPoint SingleRactorMode v43:HeapBasicObject = GuardType v8, HeapBasicObject v44:CShape = LoadField v43, :shape_id@0x1000 - v45:CShape[0x1001] = GuardBitEquals v44, CShape(0x1001) + v45:CShape[0x1001] = GuardBitEquals v44, CShape(0x1001) recompile StoreField v43, :@a@0x1002, v13 WriteBarrier v43, v13 v48:CShape[0x1003] = Const CShape(0x1003) @@ -16049,7 +16049,7 @@ mod hir_opt_tests { Jump bb8(v63) bb12(): v97:CShape = LoadField v46, :shape_id@0x103c - v98:CShape[0x103d] = GuardBitEquals v97, CShape(0x103d) + v98:CShape[0x103d] = GuardBitEquals v97, CShape(0x103d) recompile v99:BasicObject = LoadField v46, :@levar@0x103a Jump bb8(v99) bb8(v48:BasicObject): @@ -16060,7 +16060,7 @@ mod hir_opt_tests { PatchPoint NoEPEscape(set_value_loop) PatchPoint SingleRactorMode v101:CShape = LoadField v46, :shape_id@0x103c - v102:CShape[0x103e] = GuardBitEquals v101, CShape(0x103e) + v102:CShape[0x103e] = GuardBitEquals v101, CShape(0x103e) recompile StoreField v46, :@levar@0x103a, v41 WriteBarrier v46, v41 v105:CShape[0x103d] = Const CShape(0x103d) From 001a1418f2d2ed9854340adc3821fe08dbef065d Mon Sep 17 00:00:00 2001 From: Burdette Lamar Date: Thu, 21 May 2026 18:02:59 -0500 Subject: [PATCH 08/11] [DOC] Doc for Pathname#each_child --- pathname_builtin.rb | 67 ++++++++++++++++++++++----------------------- 1 file changed, 32 insertions(+), 35 deletions(-) diff --git a/pathname_builtin.rb b/pathname_builtin.rb index 51b6fd6a071d14..e9b7dad0345a74 100644 --- a/pathname_builtin.rb +++ b/pathname_builtin.rb @@ -811,42 +811,39 @@ def children(with_directory=true) result end - # Iterates over the children of the directory - # (files and subdirectories, not recursive). - # - # It yields Pathname object for each child. - # - # By default, the yielded pathnames will have enough information to access - # the files. - # - # If you set +with_directory+ to +false+, then the returned pathnames will - # contain the filename only. - # - # Pathname("/usr/local").each_child {|f| p f } - # #=> # - # # # - # # # - # # # - # # # - # # # - # # # - # # # - # - # Pathname("/usr/local").each_child(false) {|f| p f } - # #=> # - # # # - # # # - # # # - # # # - # # # - # # # - # # # - # - # Note that the results never contain the entries +.+ and +..+ in - # the directory because they are not children. - # - # See Pathname#children + # :markup: markdown + # + # call-seq: + # each_child(with_dirnames = true) {|entry| ... } -> array_of_pathnames + # each_child(with_dirnames = true) -> new_enumerator + # + # With a block given and `with_dirnames` given as `true` (the default), + # yields a new pathname for each child + # of the entry represented by `self`; + # returns an array of those pathnames: + # + # ```ruby + # Pathname('include').each_child {|child| p child } + # # + # # + # => [#, #] + # ``` # + # With a block given and `with_dirnames` given as `false`, + # yields a new pathname for each child + # of the entry represented by `self` with its dirname omitted; + # returns an array of those pathnames: + # + # ```ruby + # Pathname('include').each_child(false) {|child| p child } + # # + # # + # => [#, #] + # ``` + # + # Note that entries `'.'` and `'..'` are not children. + # + # With no block given, returns a new Enumerator. def each_child(with_directory=true, &b) children(with_directory).each(&b) end From f3467e97b197aa797996a36919c1dbe26c5c244d Mon Sep 17 00:00:00 2001 From: Burdette Lamar Date: Thu, 21 May 2026 18:03:27 -0500 Subject: [PATCH 09/11] [DOC] Doc for Pathname#unlink (#17057) --- pathname_builtin.rb | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/pathname_builtin.rb b/pathname_builtin.rb index e9b7dad0345a74..ffebbf1d4e2a27 100644 --- a/pathname_builtin.rb +++ b/pathname_builtin.rb @@ -1658,8 +1658,24 @@ def opendir(&block) # :yield: dir end class Pathname # * mixed * - # Removes a file or directory, using File.unlink or - # Dir.unlink as necessary. + # + # :markup: markdown + # + # call-seq: + # unlink -> 1 or 0 + # + # Removes the file or directory represented by `self`, using: + # + # - File.unlink, if `self` represents a file; returns `1`. + # - Dir.unlink, if `self` represents a directory; returns `0`. + # + # Examples: + # + # ```ruby + # Pathname(Tempfile.create).unlink # => 1 + # Pathname(Pathname.mktmpdir).unlink # => 0 + # ``` + # def unlink() Dir.unlink @path rescue Errno::ENOTDIR From 3903ef6407b0ecb49da6258b8bc9f80f0ac5b531 Mon Sep 17 00:00:00 2001 From: BurdetteLamar Date: Thu, 21 May 2026 16:14:32 -0500 Subject: [PATCH 10/11] [DOC] Doc for Pathname#each_entry --- pathname_builtin.rb | 23 ++++++++++++++++++++--- 1 file changed, 20 insertions(+), 3 deletions(-) diff --git a/pathname_builtin.rb b/pathname_builtin.rb index ffebbf1d4e2a27..d661865497583f 100644 --- a/pathname_builtin.rb +++ b/pathname_builtin.rb @@ -1636,10 +1636,27 @@ class << self # Pathname object. def entries() Dir.entries(@path).map {|f| self.class.new(f) } end - # Iterates over the entries (files and subdirectories) in the directory. It - # yields a Pathname object for each entry. + # :markup: markdown # - # This method has existed since 1.8.1. + # call-seq: + # each_entry {|entry| ... } -> nil + # each_entry -> new_enumerator + # + # With a block given, + # yields a new pathname for each entry + # in the entry represented by `self`; + # returns `nil`: + # + # ```ruby + # Pathname('include').each_entry {|entry| p entry } + # # + # # + # # + # # + # => nil + # ``` + # + # With no block given, returns a new Enumerator. def each_entry(&block) # :yield: pathname return to_enum(__method__) unless block_given? Dir.foreach(@path) {|f| yield self.class.new(f) } From 7012f365f9bae8085cccc7d4977764d034abcddb Mon Sep 17 00:00:00 2001 From: Peter Zhu Date: Thu, 21 May 2026 19:53:41 -0400 Subject: [PATCH 11/11] [DOC] Add comments for return values of Pathname examples --- pathname_builtin.rb | 40 ++++++++++++++++++++-------------------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/pathname_builtin.rb b/pathname_builtin.rb index d661865497583f..63426812204082 100644 --- a/pathname_builtin.rb +++ b/pathname_builtin.rb @@ -589,17 +589,17 @@ def each_filename # :yield: filename # ```ruby # # Absolute path. # Pathname('/path/to/some/file.rb').descend {|pn| p pn } - # # - # # - # # - # # - # # + # # # + # # # + # # # + # # # + # # # # # Relative path. # Pathname('path/to/some/file.rb').descend {|pn| p pn } - # # - # # - # # - # # + # # # + # # # + # # # + # # # # ``` # # With no block given, returns a new Enumerator. @@ -824,9 +824,9 @@ def children(with_directory=true) # # ```ruby # Pathname('include').each_child {|child| p child } - # # - # # - # => [#, #] + # # # + # # # + # # => [#, #] # ``` # # With a block given and `with_dirnames` given as `false`, @@ -836,9 +836,9 @@ def children(with_directory=true) # # ```ruby # Pathname('include').each_child(false) {|child| p child } - # # - # # - # => [#, #] + # # # + # # # + # # => [#, #] # ``` # # Note that entries `'.'` and `'..'` are not children. @@ -1649,11 +1649,11 @@ def entries() Dir.entries(@path).map {|f| self.class.new(f) } end # # ```ruby # Pathname('include').each_entry {|entry| p entry } - # # - # # - # # - # # - # => nil + # # # + # # # + # # # + # # # + # # => nil # ``` # # With no block given, returns a new Enumerator.