diff options
Diffstat (limited to 'zjit/src')
| -rw-r--r-- | zjit/src/backend/lir.rs | 65 | ||||
| -rw-r--r-- | zjit/src/codegen.rs | 60 | ||||
| -rw-r--r-- | zjit/src/cruby.rs | 13 | ||||
| -rw-r--r-- | zjit/src/cruby_bindings.inc.rs | 9 | ||||
| -rw-r--r-- | zjit/src/cruby_methods.rs | 8 | ||||
| -rw-r--r-- | zjit/src/hir.rs | 88 | ||||
| -rw-r--r-- | zjit/src/hir/opt_tests.rs | 537 | ||||
| -rw-r--r-- | zjit/src/hir/tests.rs | 12 | ||||
| -rw-r--r-- | zjit/src/hir_type/gen_hir_type.rb | 6 | ||||
| -rw-r--r-- | zjit/src/hir_type/hir_type.inc.rs | 77 | ||||
| -rw-r--r-- | zjit/src/hir_type/mod.rs | 29 | ||||
| -rw-r--r-- | zjit/src/profile.rs | 15 |
12 files changed, 531 insertions, 388 deletions
diff --git a/zjit/src/backend/lir.rs b/zjit/src/backend/lir.rs index 467cd5b4de..a8d03ad69a 100644 --- a/zjit/src/backend/lir.rs +++ b/zjit/src/backend/lir.rs @@ -1812,8 +1812,9 @@ impl Assembler Opnd::Reg(ALLOC_REGS[idx]) } else { // With FrameSetup, the address that NATIVE_BASE_PTR points to stores an old value in the register. - // To avoid clobbering it, we need to start from the next slot, hence `+ 1` for the index. - Opnd::mem(64, NATIVE_BASE_PTR, (idx - ALLOC_REGS.len() + 1) as i32 * -SIZEOF_VALUE_I32) + // To avoid clobbering it, we need to start from the next slot, and we also reserve one space for + // JITFrame, hence `+ 2` for the index. + Opnd::mem(64, NATIVE_BASE_PTR, (idx - ALLOC_REGS.len() + 2) as i32 * -SIZEOF_VALUE_I32) } } @@ -2661,24 +2662,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 +2677,34 @@ 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<SideExitReason>) { + // 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() { + // Clear cfp->jit_return to prepare for a C call. Normally, cfp->jit_return + // is cleared by the caller jit_exec() or JIT_EXEC(), but if we're about to + // make a C call, we need to clear any stale JITFrame. + 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 +2781,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 +2801,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/codegen.rs b/zjit/src/codegen.rs index f4d62fc7bc..4eee769315 100644 --- a/zjit/src/codegen.rs +++ b/zjit/src/codegen.rs @@ -19,7 +19,7 @@ use crate::state::ZJITState; use crate::stats::{CompileError, exit_counter_for_compile_error, exit_counter_for_unhandled_hir_insn, incr_counter, incr_counter_by, send_fallback_counter, send_fallback_counter_for_method_type, send_fallback_counter_for_super_method_type, send_fallback_counter_ptr_for_opcode, send_without_block_fallback_counter_for_method_type, send_without_block_fallback_counter_for_optimized_method_type}; use crate::stats::{counter_ptr, with_time_stat, trace_compile_phase, Counter, Counter::{compile_time_ns, exit_compile_error}}; use crate::{asm::CodeBlock, cruby::*, options::debug, virtualmem::CodePtr}; -use crate::backend::lir::{self, Assembler, C_ARG_OPNDS, C_RET_OPND, CFP, EC, NATIVE_STACK_PTR, Opnd, SP, SideExit, SideExitRecompile, Target, asm_ccall, asm_comment}; +use crate::backend::lir::{self, Assembler, C_ARG_OPNDS, C_RET_OPND, CFP, EC, NATIVE_BASE_PTR, NATIVE_STACK_PTR, Opnd, SP, SideExit, SideExitRecompile, Target, asm_ccall, asm_comment}; use crate::hir::{iseq_to_hir, BlockId, Invariant, RangeType, SideExitReason::{self, *}, SpecialBackrefSymbol, SpecialObjectType}; use crate::hir::{BlockHandler, Const, FieldName, FrameState, Function, Insn, InsnId, Recompile, SendFallbackReason}; use crate::hir_type::{types, Type}; @@ -376,7 +376,7 @@ fn gen_function(cb: &mut CodeBlock, iseq: IseqPtr, version: IseqVersionRef, func let (mut jit, asm) = trace_compile_phase("codegen", || { let num_spilled_params = max_num_params(function).saturating_sub(ALLOC_REGS.len()); let mut jit = JITState::new(version, function.num_insns(), function.num_blocks()); - let mut asm = Assembler::new_with_stack_slots(num_spilled_params); + let mut asm = Assembler::new_with_stack_slots(num_spilled_params + 1); // +1 for JITFrame // Mapping from HIR block IDs to LIR block IDs. // This is is a one-to-one mapping from HIR to LIR blocks used for finding @@ -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 } @@ -2111,19 +2111,17 @@ fn gen_new_hash( elements: Vec<Opnd>, state: &FrameState, ) -> lir::Opnd { - gen_prepare_non_leaf_call(jit, asm, state); - - let cap: c_long = elements.len().try_into().expect("Unable to fit length of elements into c_long"); - let new_hash = asm_ccall!(asm, rb_hash_new_with_size, lir::Opnd::Imm(cap)); + if elements.is_empty() { + gen_prepare_leaf_call_with_gc(asm, state); + asm_ccall!(asm, rb_hash_new,) + } else { + gen_prepare_non_leaf_call(jit, asm, state); - if !elements.is_empty() { let argv = gen_push_opnds(asm, &elements); - asm_ccall!(asm, rb_hash_bulk_insert, elements.len().into(), argv, new_hash); - + let hash = asm_ccall!(asm, rb_hash_new_with_bulk_insert, elements.len().into(), argv); gen_pop_opnds(asm, &elements); + hash } - - new_hash } /// Compile a new range instruction @@ -2186,6 +2184,17 @@ fn gen_entry_point(jit: &mut JITState, asm: &mut Assembler, jit_entry_idx: Optio }); } asm.frame_setup(&[]); + + // Publish the JITFrame slot's location via cfp->jit_return. The slot at + // [NATIVE_BASE_PTR - 8] is left uninitialized here; the JIT design relies on + // gen_save_pc_for_gc() to populate it before any C call, and on cross-ractor + // barriers ensuring that no other ractor scans this CFP before such a call. + asm.mov(Opnd::mem(64, CFP, RUBY_OFFSET_CFP_JIT_RETURN), NATIVE_BASE_PTR); + + // Poison the JITFrame slot. It should be read only after gen_save_pc_for_gc(). + if let Some(jit_return_poison) = JIT_RETURN_POISON { + asm.mov(Opnd::mem(64, NATIVE_BASE_PTR, -SIZEOF_VALUE_I32), jit_return_poison.into()); + } } /// Compile code that exits from JIT code with a return value @@ -2710,11 +2719,16 @@ fn gen_save_pc_for_gc(asm: &mut Assembler, state: &FrameState) { gen_incr_counter(asm, Counter::vm_write_jit_frame_count); asm_comment!(asm, "save JITFrame to CFP"); - if let Some(pc) = PC_POISON { - asm.mov(Opnd::mem(64, CFP, RUBY_OFFSET_CFP_PC), Opnd::const_ptr(pc)); - } let jit_frame = JITFrame::new_iseq(next_pc, state.iseq, !iseq_may_write_block_code(state.iseq)); - asm.mov(Opnd::mem(64, CFP, RUBY_OFFSET_CFP_JIT_RETURN), Opnd::const_ptr(jit_frame)); + asm.mov(Opnd::mem(64, NATIVE_BASE_PTR, -SIZEOF_VALUE_I32), Opnd::const_ptr(jit_frame)); + + // CFP_PC for a live JIT frame routes through the JITFrame on the native + // stack (cfp->jit_return points to NATIVE_BASE_PTR), so we don't need to + // touch cfp->pc here. Poisoning cfp->pc with PC_POISON would actively + // break the case where rb_zjit_materialize_frames() previously copied + // jit_frame->pc into cfp->pc and cleared cfp->jit_return: the JIT keeps + // running, lands on this routine again, and the poison would replace + // the valid materialized pc behind the GC's back. } /// Save the current PC on the CFP as a preparation for calling a C function @@ -2843,9 +2857,7 @@ fn gen_push_frame(asm: &mut Assembler, argc: usize, state: &FrameState, frame: C if frame.iseq.is_some() { // PC, SP, and ISEQ are written lazily by the callee on side-exits, non-leaf calls, or GC. - if let Some(jit_return_poison) = JIT_RETURN_POISON { - asm.mov(cfp_opnd(RUBY_OFFSET_CFP_JIT_RETURN), jit_return_poison.into()); - } + // cfp->jit_return will be written by gen_entry_point() on the callee after this frame push. if frame.write_block_code { asm_comment!(asm, "write block_code for iseq that may use it"); asm.mov(cfp_opnd(RUBY_OFFSET_CFP_BLOCK_CODE), 0.into()); @@ -3387,11 +3399,7 @@ fn gen_toregexp(jit: &mut JITState, asm: &mut Assembler, opt: usize, values: Vec gen_prepare_non_leaf_call(jit, asm, state); let first_opnd_ptr = gen_push_opnds(asm, &values); - - let tmp_ary = asm_ccall!(asm, rb_ary_tmp_new_from_values, Opnd::Imm(0), values.len().into(), first_opnd_ptr); - let result = asm_ccall!(asm, rb_reg_new_ary, tmp_ary, opt.into()); - asm_ccall!(asm, rb_ary_clear, tmp_ary); - + let result = asm_ccall!(asm, rb_reg_new_from_values, values.len().into(), first_opnd_ptr, opt.into()); gen_pop_opnds(asm, &values); result diff --git a/zjit/src/cruby.rs b/zjit/src/cruby.rs index 6a5dd234bb..14db7c57d7 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<String> { + // 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/cruby_bindings.inc.rs b/zjit/src/cruby_bindings.inc.rs index 860e1726db..5a7c3de606 100644 --- a/zjit/src/cruby_bindings.inc.rs +++ b/zjit/src/cruby_bindings.inc.rs @@ -462,6 +462,7 @@ pub struct vm_ifunc { pub data: *const ::std::os::raw::c_void, pub argc: vm_ifunc_argc, } +pub type rb_atomic_t = ::std::os::raw::c_uint; pub const METHOD_VISI_UNDEF: rb_method_visibility_t = 0; pub const METHOD_VISI_PUBLIC: rb_method_visibility_t = 1; pub const METHOD_VISI_PRIVATE: rb_method_visibility_t = 2; @@ -1535,7 +1536,7 @@ pub type vm_call_flag_bits = u32; #[repr(C)] pub struct rb_callinfo_kwarg { pub keyword_len: ::std::os::raw::c_int, - pub references: ::std::os::raw::c_int, + pub references: rb_atomic_t, pub keywords: __IncompleteArrayField<VALUE>, } #[repr(C)] @@ -2059,6 +2060,11 @@ unsafe extern "C" { pub fn rb_class_allocate_instance(klass: VALUE) -> VALUE; pub fn rb_obj_equal(obj1: VALUE, obj2: VALUE) -> VALUE; pub fn rb_reg_new_ary(ary: VALUE, options: ::std::os::raw::c_int) -> VALUE; + pub fn rb_reg_new_from_values( + cnt: ::std::os::raw::c_long, + elements: *const VALUE, + opt: ::std::os::raw::c_int, + ) -> VALUE; pub fn rb_ary_tmp_new_from_values( arg1: VALUE, arg2: ::std::os::raw::c_long, @@ -2131,6 +2137,7 @@ unsafe extern "C" { arg: st_data_t, ) -> ::std::os::raw::c_int; pub fn rb_hash_new_with_size(size: st_index_t) -> VALUE; + pub fn rb_hash_new_with_bulk_insert(argc: ::std::os::raw::c_long, argv: *const VALUE) -> VALUE; pub fn rb_hash_resurrect(hash: VALUE) -> VALUE; pub fn rb_hash_stlike_lookup( hash: VALUE, diff --git a/zjit/src/cruby_methods.rs b/zjit/src/cruby_methods.rs index db09930721..05b0055032 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 e5976a4045..93dbbec690 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); @@ -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)) }, @@ -3323,10 +3329,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<TypeDistributionSummary> { @@ -5088,6 +5102,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() { @@ -5107,6 +5130,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) { @@ -6572,16 +6607,14 @@ impl ProfileOracle { } /// Map the interpreter-recorded types of the stack onto the HIR operands on our compile-time virtual stack. - /// `stack_offset` is the number of extra stack entries above the profiled operands (e.g. 1 for - /// sends with ARGS_BLOCKARG, where the block arg sits on top of the regular args). - fn profile_stack(&mut self, state: &FrameState, stack_offset: usize) { + fn profile_stack(&mut self, state: &FrameState) { let iseq_insn_idx = state.insn_idx; let Some(operand_types) = self.payload.profile.get_operand_types(iseq_insn_idx) else { return }; let entry = self.types.entry(iseq_insn_idx).or_default(); // operand_types is always going to be <= stack size (otherwise it would have an underflow // at run-time) so use that to drive iteration. for (idx, insn_type_distribution) in operand_types.iter().rev().enumerate() { - let insn = state.stack_topn(idx + stack_offset).expect("Unexpected stack underflow in profiling"); + let insn = state.stack_topn(idx).expect("Unexpected stack underflow in profiling"); entry.push((insn, TypeDistributionSummary::new(insn_type_distribution))) } } @@ -6784,17 +6817,7 @@ pub fn iseq_to_hir(iseq: *const rb_iseq_t) -> Result<Function, ParseError> { } } else { - // For sends with ARGS_BLOCKARG, the block arg sits on the stack above - // the profiled operands (receiver + regular args). Skip it so that the - // profile types map onto the correct HIR operands. - let stack_offset = if opcode == YARVINSN_send || opcode == YARVINSN_opt_send_without_block { - let cd: *const rb_call_data = get_arg(pc, 0).as_ptr(); - let flags = unsafe { vm_ci_flag(rb_get_call_data_ci(cd)) }; - usize::from(flags & VM_CALL_ARGS_BLOCKARG != 0) - } else { - 0 - }; - profiles.profile_stack(&exit_state, stack_offset); + profiles.profile_stack(&exit_state); } // Flag a future getlocal/setlocal to add a patch point if this instruction is not leaf. @@ -7220,7 +7243,10 @@ pub fn iseq_to_hir(iseq: *const rb_iseq_t) -> Result<Function, ParseError> { // Check if #new resolves to rb_class_new_instance_pass_kw. // TODO: Guard on a profiled class and add a patch point for #new redefinition - let argc = unsafe { vm_ci_argc((*cd).ci) } as usize; + let argc = crate::profile::num_arguments_on_stack(cd); + let ci = unsafe { get_call_data_ci(cd) }; + let flags = unsafe { rb_vm_ci_flag(ci) }; + assert_eq!(flags & VM_CALL_ARGS_BLOCKARG, 0); let val = state.stack_topn(argc)?; let test_id = fun.push_insn(block, Insn::IsMethodCfunc { val, cd, cfunc: rb_class_new_instance_pass_kw as *const u8, state: exit_id }); @@ -7646,7 +7672,8 @@ pub fn iseq_to_hir(iseq: *const rb_iseq_t) -> Result<Function, ParseError> { fun.push_insn(block, Insn::SideExit { state: exit_id, reason: SideExitReason::UnhandledCallType(call_type), recompile: None }); break; // End the block } - let argc = unsafe { vm_ci_argc((*cd).ci) }; + let argc = crate::profile::num_arguments_on_stack(cd); + assert_eq!(flags & VM_CALL_ARGS_BLOCKARG, 0); // Side-exit send fallbacks while tracing to avoid FLAG_FINISH breaking throw TAG_RETURN semantics if unsafe { rb_zjit_iseq_tracing_currently_enabled() } { @@ -7751,7 +7778,7 @@ pub fn iseq_to_hir(iseq: *const rb_iseq_t) -> Result<Function, ParseError> { fun.push_insn(block, Insn::SideExit { state: exit_id, reason: SideExitReason::UnhandledCallType(call_type), recompile: None }); break; // End the block } - let argc = unsafe { vm_ci_argc((*cd).ci) }; + let argc = crate::profile::num_arguments_on_stack(cd); let mid = unsafe { rb_vm_ci_mid(call_info) }; // Check for calls to directives @@ -7885,10 +7912,9 @@ pub fn iseq_to_hir(iseq: *const rb_iseq_t) -> Result<Function, ParseError> { fun.push_insn(block, Insn::SideExit { state: exit_id, reason: SideExitReason::SendWhileTracing, recompile: None }); break; } - let argc = unsafe { vm_ci_argc((*cd).ci) }; let block_arg = (flags & VM_CALL_ARGS_BLOCKARG) != 0; - let args = state.stack_pop_n(argc as usize + usize::from(block_arg))?; + let args = state.stack_pop_n(crate::profile::num_arguments_on_stack(cd))?; let recv = state.stack_pop()?; let block_handler = if !blockiseq.is_null() { Some(BlockHandler::BlockIseq(blockiseq)) @@ -7985,9 +8011,7 @@ pub fn iseq_to_hir(iseq: *const rb_iseq_t) -> Result<Function, ParseError> { fun.push_insn(block, Insn::SideExit { state: exit_id, reason: SideExitReason::SendWhileTracing, recompile: None }); break; } - let argc = unsafe { vm_ci_argc((*cd).ci) }; - let block_arg = (flags & VM_CALL_ARGS_BLOCKARG) != 0; - let args = state.stack_pop_n(argc as usize + usize::from(block_arg))?; + let args = state.stack_pop_n(crate::profile::num_arguments_on_stack(cd))?; let recv = state.stack_pop()?; let blockiseq: IseqPtr = get_arg(pc, 1).as_ptr(); let result = fun.push_insn(block, Insn::InvokeSuper { recv, cd, blockiseq, args, state: exit_id, reason: Uncategorized(opcode) }); @@ -8079,9 +8103,7 @@ pub fn iseq_to_hir(iseq: *const rb_iseq_t) -> Result<Function, ParseError> { fun.push_insn(block, Insn::SideExit { state: exit_id, reason: SideExitReason::SendWhileTracing, recompile: None }); break; } - let argc = unsafe { vm_ci_argc((*cd).ci) }; - let block_arg = (flags & VM_CALL_ARGS_BLOCKARG) != 0; - let args = state.stack_pop_n(argc as usize + usize::from(block_arg))?; + let args = state.stack_pop_n(crate::profile::num_arguments_on_stack(cd))?; // Check if this is a monomorphic IFUNC block handler we can specialize let block_handler_types = profiles.payload.profile.get_operand_types(exit_state.insn_idx); @@ -8327,7 +8349,7 @@ pub fn iseq_to_hir(iseq: *const rb_iseq_t) -> Result<Function, ParseError> { } YARVINSN_objtostring => { let cd: *const rb_call_data = get_arg(pc, 0).as_ptr(); - let argc = unsafe { vm_ci_argc((*cd).ci) }; + let argc = crate::profile::num_arguments_on_stack(cd); assert_eq!(0, argc, "objtostring should not have args"); let recv = state.stack_pop()?; diff --git a/zjit/src/hir/opt_tests.rs b/zjit/src/hir/opt_tests.rs index 3435347d33..4a83b40e38 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 "); } @@ -1076,9 +1076,8 @@ 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) + v28:CInt64 = AdjustBounds v32, v33 v29:CInt64[0] = Const CInt64(0) v30:CInt64 = GuardGreaterEq v28, v29 v31:BasicObject = ArrayAref v11, v30 @@ -1905,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 "); } @@ -2105,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 "); } @@ -3207,10 +3206,10 @@ 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 + v34:StringExact|NilClass = CCall v29, :Module#name@0x1048 PatchPoint NoEPEscape(test) v21:Fixnum[1] = Const Value(1) CheckInterrupts @@ -3266,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 "); @@ -3291,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 @@ -3323,9 +3322,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:ModuleSubclass[Kernel@0x1018] = Const Value(VALUE(0x1018)) v14:ArrayExact = NewArray v22, v25 CheckInterrupts Return v14 @@ -3353,7 +3352,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,12 +3574,12 @@ 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)) + v26:ClassSubclass[Module@0x1010] = Const Value(VALUE(0x1010)) CheckInterrupts - Return v25 + Return v26 "); } @@ -4392,7 +4391,7 @@ mod hir_opt_tests { bb3(v6:BasicObject): PatchPoint SingleRactorMode PatchPoint StableConstantNames(0x1000, Kernel) - v18:ModuleExact[VALUE(0x1008)] = Const Value(VALUE(0x1008)) + v18:ModuleSubclass[Kernel@0x1008] = Const Value(VALUE(0x1008)) CheckInterrupts Return v18 "); @@ -4423,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 "); @@ -4449,13 +4448,13 @@ 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) 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 "); @@ -4485,14 +4484,14 @@ 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) 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 "); @@ -4517,13 +4516,13 @@ 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) 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 "); @@ -4548,13 +4547,13 @@ 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) 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 "); @@ -4579,14 +4578,14 @@ 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) 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 "); @@ -4612,14 +4611,14 @@ 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) 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 "); } @@ -4642,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 @@ -4674,13 +4673,13 @@ 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) - v53:BasicObject = CCallVariadic v43, :String.new@0x1040 + v54:BasicObject = CCallVariadic v43, :String.new@0x1040 CheckInterrupts - Return v53 + Return v54 "); } @@ -4703,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 @@ -4711,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 "); @@ -4737,11 +4736,11 @@ 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) + v24:ObjectSubclass[class_exact:C] = ObjectAllocClass C:VALUE(0x1008) CheckInterrupts - Return v23 + Return v24 "); } @@ -4767,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 @@ -4797,11 +4796,11 @@ 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 + v24:BasicObject = CCallWithFrame v20, :Class.allocate@0x1048 CheckInterrupts - Return v23 + Return v24 "); } @@ -5270,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 @@ -5296,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 @@ -5614,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 @@ -5643,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) @@ -5688,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) @@ -6368,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 @@ -6459,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. @@ -6491,12 +6490,9 @@ 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) - v26:CInt64 = ArrayLength v11 - v27:CInt64[1] = GuardLess v32, v26 - v33:Fixnum[5] = Const Value(5) + v34:Fixnum[5] = Const Value(5) CheckInterrupts - Return v33 + Return v34 "); } @@ -6522,9 +6518,8 @@ 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) + v28:CInt64 = AdjustBounds v32, v33 v29:CInt64[0] = Const CInt64(0) v30:CInt64 = GuardGreaterEq v28, v29 v31:BasicObject = ArrayAref v11, v30 @@ -6555,9 +6550,8 @@ 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) + 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) - v26:CInt64 = ArrayLength v11 - v27:CInt64[10] = GuardLess v32, v26 - v33:NilClass = Const Value(nil) - CheckInterrupts - Return v33 + SideExit GuardLess "); } @@ -6840,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 @@ -7509,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 @@ -7777,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 @@ -7849,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 @@ -7914,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 @@ -7949,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 @@ -8186,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) @@ -8454,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 "); } @@ -8495,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 @@ -8744,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) recompile + v28:NilClass = Const Value(nil) CheckInterrupts - Return v27 + Return v28 "); } @@ -8780,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) recompile + v28:NilClass = Const Value(nil) CheckInterrupts - Return v27 + Return v28 "); } @@ -8817,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 @@ -8853,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 @@ -9347,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 "); @@ -9593,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 "); } @@ -9722,13 +9711,13 @@ 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 - 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 "); } @@ -12413,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 "); } @@ -12445,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 "); } @@ -12478,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 "); } @@ -12511,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 "); } @@ -12543,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 "); } @@ -12575,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 "); } @@ -12617,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 "); } @@ -12685,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 "); } @@ -12979,12 +12968,12 @@ 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 + v31:BoolExact = IsA v10, v27 CheckInterrupts - Return v30 + Return v31 "); } @@ -13010,12 +12999,12 @@ mod hir_opt_tests { bb3(v9:BasicObject, v10:BasicObject): PatchPoint SingleRactorMode PatchPoint StableConstantNames(0x1008, Kernel) - v27:ModuleExact[VALUE(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 + v31:BoolExact = CCall v27, :Module#===@0x1050, v10 CheckInterrupts - Return v30 + Return v31 "); } @@ -13041,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 @@ -13073,7 +13062,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: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 @@ -13108,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 @@ -13143,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) @@ -13174,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 @@ -13206,7 +13195,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: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 @@ -13241,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 @@ -13271,11 +13260,11 @@ 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) + v27:TrueClass = Const Value(true) CheckInterrupts - Return v26 + Return v27 "); } @@ -13299,11 +13288,11 @@ 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) + v27:FalseClass = Const Value(false) CheckInterrupts - Return v26 + Return v27 "); } @@ -13330,12 +13319,12 @@ 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) + v31:TrueClass = Const Value(true) CheckInterrupts - Return v30 + Return v31 "); } @@ -13362,12 +13351,12 @@ 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) + v31:TrueClass = Const Value(true) CheckInterrupts - Return v30 + Return v31 "); } @@ -13393,11 +13382,11 @@ 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) + v30:TrueClass = Const Value(true) CheckInterrupts - Return v29 + Return v30 "); } @@ -13516,12 +13505,12 @@ 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) 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 @@ -13552,11 +13541,11 @@ 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 + v32:StringExact|NilClass = CCall v28, :Module#name@0x1078 CheckInterrupts - Return v31 + Return v32 "); } @@ -13584,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 "); @@ -13609,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:Class[Integer@0x1000] = Const Value(VALUE(0x1000)) + v22:ClassSubclass[Integer@0x1000] = Const Value(VALUE(0x1000)) CheckInterrupts - Return v21 + Return v22 "); } @@ -13634,13 +13623,43 @@ 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 "); } #[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@<compiled>: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) + v37:ModuleExact[VALUE(0x1048)] = Const Value(VALUE(0x1048)) + CheckInterrupts + Return v37 + "); + } + + #[test] fn no_load_from_ep_right_after_entrypoint() { let formatted = eval(" def read_nil_local(a, _b, _c) @@ -13683,16 +13702,16 @@ 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 + 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 "); } @@ -13729,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 "); } @@ -13770,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 "); } @@ -13809,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 "); } @@ -13848,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 "); } @@ -13887,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) recompile + v28:BasicObject = LoadField v20, :@a@0x104a CheckInterrupts - Return v27 + Return v28 "); } @@ -13928,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 "); } @@ -13967,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 "); } @@ -14006,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 "); } @@ -14046,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 @@ -14087,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 "); } @@ -14122,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 "); } @@ -15530,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) @@ -15578,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) @@ -15587,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 @@ -15626,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) @@ -15895,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 "); } @@ -16030,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): @@ -16041,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) @@ -16509,20 +16528,80 @@ 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 - CheckInterrupts - Return v215 + 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 v225 + "); + } + + #[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@<compiled>: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) + v26:CInt64 = ArrayLength v21 + v27:Fixnum = BoxFixnum v26 + CheckInterrupts + Return v27 + "); + } + + #[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@<compiled>: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) + v28:CInt64[4] = Const CInt64(4) + v27:Fixnum = BoxFixnum v28 + CheckInterrupts + Return v27 "); } } diff --git a/zjit/src/hir/tests.rs b/zjit/src/hir/tests.rs index 566cf7aeb7..7a1cd85c5f 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 "); } @@ -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 e9d925ba0f..41f96a7a82 100644 --- a/zjit/src/hir_type/gen_hir_type.rb +++ b/zjit/src/hir_type/gen_hir_type.rb @@ -75,8 +75,8 @@ $inexact_c_names = { # 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 @@ base_type "Regexp", c_name: "rb_cRegexp" 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 8b95914ddf..f4c3bf3489 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 206b74e7d3..cdfc15a935 100644 --- a/zjit/src/hir_type/mod.rs +++ b/zjit/src/hir_type/mod.rs @@ -2,13 +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; @@ -80,6 +80,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) } => @@ -169,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 { @@ -212,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 } diff --git a/zjit/src/profile.rs b/zjit/src/profile.rs index d8bcd50d96..08803c4570 100644 --- a/zjit/src/profile.rs +++ b/zjit/src/profile.rs @@ -95,9 +95,9 @@ fn profile_insn(bare_opcode: ruby_vminsn_type, ec: EcPtr) { YARVINSN_invokesuper => profile_invokesuper(profiler, profile), YARVINSN_opt_send_without_block | YARVINSN_send => { let cd: *const rb_call_data = profiler.insn_opnd(0).as_ptr(); - let argc = unsafe { vm_ci_argc((*cd).ci) }; + let argc = num_arguments_on_stack(cd); // Profile all the arguments and self (+1). - profile_operands(profiler, profile, (argc + 1) as usize); + profile_operands(profiler, profile, argc + 1); } YARVINSN_splatkw => profile_operands(profiler, profile, 2), _ => {} @@ -111,6 +111,15 @@ fn profile_insn(bare_opcode: ruby_vminsn_type, ec: EcPtr) { } } +/// Return the argc as stated in the calldata plus: +/// * 1 if there is an explicit blockarg, since that will be passed on the stack +pub fn num_arguments_on_stack(cd: *const rb_call_data) -> usize { + let ci = unsafe { rb_get_call_data_ci(cd) }; + let flags = unsafe { rb_vm_ci_flag(ci) }; + let has_blockarg = (flags & VM_CALL_ARGS_BLOCKARG) != 0; + (unsafe { vm_ci_argc(ci) }) as usize + has_blockarg as usize +} + const DISTRIBUTION_SIZE: usize = 4; pub type TypeDistribution = Distribution<ProfiledType, DISTRIBUTION_SIZE>; @@ -184,7 +193,7 @@ fn profile_invokesuper(profiler: &mut Profiler, profile: &mut IseqProfile) { unsafe { rb_gc_writebarrier(profiler.iseq.into(), cme_value) }; let cd: *const rb_call_data = profiler.insn_opnd(0).as_ptr(); - let argc = unsafe { vm_ci_argc((*cd).ci) }; + let argc = num_arguments_on_stack(cd); // Profile all the arguments and self (+1). profile_operands(profiler, profile, (argc + 1) as usize); |
