From 51d4c2c465e3ce708dddcd65d93e6e29225b454d Mon Sep 17 00:00:00 2001 From: Xavier Noria Date: Mon, 9 Feb 2026 13:46:05 +0100 Subject: [PATCH 1/9] [ruby/rubygems] Document gemspecs must be deterministic https://github.com/ruby/rubygems/commit/7cab5cd68f --- lib/rubygems/specification.rb | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/lib/rubygems/specification.rb b/lib/rubygems/specification.rb index aa495696adce3c..64f289a7b41e50 100644 --- a/lib/rubygems/specification.rb +++ b/lib/rubygems/specification.rb @@ -34,6 +34,15 @@ # Starting in RubyGems 2.0, a Specification can hold arbitrary # metadata. See #metadata for restrictions on the format and size of metadata # items you may add to a specification. +# +# Specifications must be deterministic, as in the example above. For instance, +# you cannot define attributes conditionally: +# +# # INVALID: do not do this. +# unless RUBY_ENGINE == "jruby" +# s.extensions << "ext/example/extconf.rb" +# end +# class Gem::Specification < Gem::BasicSpecification # REFACTOR: Consider breaking out this version stuff into a separate From 9efe557da5e17fc860f40956192e9df2e6bd1e6c Mon Sep 17 00:00:00 2001 From: Max Bernstein Date: Mon, 9 Feb 2026 16:40:25 -0500 Subject: [PATCH 2/9] ZJIT: Drop has_blockiseq guard on local variable access (#16113) So `has_blockiseq` was masking two other bugs. First, that we had only a static EP escape check for an iseq. We need to check both that an iseq is not known to statically escape them *and that it has not escaped them before*. Only then can we use SSA locals. This popped up in a weird PP unit test that was reducible to the test cases added in the PR. So I fixed that. Second, that we didn't VM lock when code patching for EP escape. That showed up in the Ractor btests in my previous PRs #14600 and #15254 #15316, and I had no idea what was going on at the time, so I dropped it. But if we add this lock, like all the other invariant patching cases have, the problem goes away. Seems reasonable. Finally, we can use normal full SSA locals even in the presence of a blockiseq. We still reload all of them after each send-with-block, but we can in a later PR reduce that to just the locals that are (recursively) visibly written to. --- zjit/src/hir.rs | 24 ++++---- zjit/src/hir/opt_tests.rs | 120 +++++++++++++++++++++++++++++++++----- zjit/src/invariants.rs | 24 ++++---- 3 files changed, 128 insertions(+), 40 deletions(-) diff --git a/zjit/src/hir.rs b/zjit/src/hir.rs index 7812c6058e3cb2..3fd5fcc5cbf4ea 100644 --- a/zjit/src/hir.rs +++ b/zjit/src/hir.rs @@ -6087,14 +6087,12 @@ pub fn jit_entry_insns(iseq: IseqPtr) -> Vec { struct BytecodeInfo { jump_targets: Vec, - has_blockiseq: bool, } fn compute_bytecode_info(iseq: *const rb_iseq_t, opt_table: &[u32]) -> BytecodeInfo { let iseq_size = unsafe { get_iseq_encoded_size(iseq) }; let mut insn_idx = 0; let mut jump_targets: HashSet = opt_table.iter().copied().collect(); - let mut has_blockiseq = false; while insn_idx < iseq_size { // Get the current pc and opcode let pc = unsafe { rb_iseq_pc_at_idx(iseq, insn_idx) }; @@ -6118,18 +6116,12 @@ fn compute_bytecode_info(iseq: *const rb_iseq_t, opt_table: &[u32]) -> BytecodeI jump_targets.insert(insn_idx); } } - YARVINSN_send | YARVINSN_invokesuper => { - let blockiseq: IseqPtr = get_arg(pc, 1).as_iseq(); - if !blockiseq.is_null() { - has_blockiseq = true; - } - } _ => {} } } let mut result = jump_targets.into_iter().collect::>(); result.sort(); - BytecodeInfo { jump_targets: result, has_blockiseq } + BytecodeInfo { jump_targets: result } } #[derive(Debug, PartialEq, Clone, Copy)] @@ -6244,7 +6236,7 @@ pub fn iseq_to_hir(iseq: *const rb_iseq_t) -> Result { // Compute a map of PC->Block by finding jump targets let jit_entry_insns = jit_entry_insns(iseq); - let BytecodeInfo { jump_targets, has_blockiseq } = compute_bytecode_info(iseq, &jit_entry_insns); + let BytecodeInfo { jump_targets } = compute_bytecode_info(iseq, &jit_entry_insns); // Make all empty basic blocks. The ordering of the BBs matters for getting fallthrough jumps // in good places, but it's not necessary for correctness. TODO: Higher quality scheduling during lowering. @@ -6276,7 +6268,11 @@ pub fn iseq_to_hir(iseq: *const rb_iseq_t) -> Result { // Check if the EP is escaped for the ISEQ from the beginning. We give up // optimizing locals in that case because they're shared with other frames. - let ep_escaped = iseq_escapes_ep(iseq); + let ep_starts_escaped = iseq_escapes_ep(iseq); + // Check if the EP has been escaped at some point in the ISEQ. If it has, then we assume that + // its EP is shared with other frames. + let ep_has_been_escaped = crate::invariants::iseq_escapes_ep(iseq); + let ep_escaped = ep_starts_escaped || ep_has_been_escaped; // Iteratively fill out basic blocks using a queue. // TODO(max): Basic block arguments at edges @@ -6620,7 +6616,7 @@ pub fn iseq_to_hir(iseq: *const rb_iseq_t) -> Result { // Use FrameState to get kw_bits when possible, just like getlocal_WC_0. let val = if !local_inval { state.getlocal(ep_offset) - } else if ep_escaped || has_blockiseq { + } else if ep_escaped { fun.push_insn(block, Insn::GetLocal { ep_offset, level: 0, use_sp: false, rest_param: false }) } else { let exit_id = fun.push_insn(block, Insn::Snapshot { state: exit_state.without_locals() }); @@ -6743,7 +6739,7 @@ pub fn iseq_to_hir(iseq: *const rb_iseq_t) -> Result { // In case of JIT-to-JIT send locals might never end up in EP memory. let val = state.getlocal(ep_offset); state.stack_push(val); - } else if ep_escaped || has_blockiseq { // TODO: figure out how to drop has_blockiseq here + } else if ep_escaped { // Read the local using EP let val = fun.push_insn(block, Insn::GetLocal { ep_offset, level: 0, use_sp: false, rest_param: false }); state.setlocal(ep_offset, val); // remember the result to spill on side-exits @@ -6764,7 +6760,7 @@ pub fn iseq_to_hir(iseq: *const rb_iseq_t) -> Result { YARVINSN_setlocal_WC_0 => { let ep_offset = get_arg(pc, 0).as_u32(); let val = state.stack_pop()?; - if ep_escaped || has_blockiseq { // TODO: figure out how to drop has_blockiseq here + if ep_escaped { // Write the local using EP fun.push_insn(block, Insn::SetLocal { val, ep_offset, level: 0 }); } else if local_inval { diff --git a/zjit/src/hir/opt_tests.rs b/zjit/src/hir/opt_tests.rs index d0d2e190784d4b..afbbc8bedc39e1 100644 --- a/zjit/src/hir/opt_tests.rs +++ b/zjit/src/hir/opt_tests.rs @@ -2926,15 +2926,14 @@ mod hir_opt_tests { Jump bb2(v5, v6) bb2(v8:BasicObject, v9:NilClass): v13:Fixnum[1] = Const Value(1) - SetLocal :a, l0, EP@3, v13 PatchPoint NoSingletonClass(Object@0x1000) PatchPoint MethodRedefined(Object@0x1000, foo@0x1008, cme:0x1010) v31:HeapObject[class_exact*:Object@VALUE(0x1000)] = GuardType v8, HeapObject[class_exact*:Object@VALUE(0x1000)] IncrCounter inline_iseq_optimized_send_count - v20:BasicObject = GetLocal :a, l0, EP@3 - v24:BasicObject = GetLocal :a, l0, EP@3 + v19:BasicObject = GetLocal :a, l0, EP@3 + PatchPoint NoEPEscape(test) CheckInterrupts - Return v24 + Return v19 "); } @@ -3415,15 +3414,14 @@ mod hir_opt_tests { Jump bb2(v6, v7, v8) bb2(v10:BasicObject, v11:BasicObject, v12:NilClass): v16:ArrayExact = NewArray - SetLocal :a, l0, EP@3, v16 - v22:TrueClass = Const Value(true) + v21:TrueClass = Const Value(true) IncrCounter complex_arg_pass_caller_kwarg - v24:BasicObject = Send v11, 0x1000, :each_line, v22 # SendFallbackReason: Complex argument passing - v25:BasicObject = GetLocal :s, l0, EP@4 - v26:BasicObject = GetLocal :a, l0, EP@3 - v30:BasicObject = GetLocal :a, l0, EP@3 + v23:BasicObject = Send v11, 0x1000, :each_line, v21 # SendFallbackReason: Complex argument passing + v24:BasicObject = GetLocal :s, l0, EP@4 + v25:BasicObject = GetLocal :a, l0, EP@3 + PatchPoint NoEPEscape(test) CheckInterrupts - Return v30 + Return v25 "); } @@ -6523,7 +6521,6 @@ mod hir_opt_tests { Jump bb2(v5, v6) bb2(v8:BasicObject, v9:NilClass): v13:ArrayExact = NewArray - SetLocal :result, l0, EP@3, v13 PatchPoint SingleRactorMode PatchPoint StableConstantNames(0x1000, A) v36:ArrayExact[VALUE(0x1008)] = Const Value(VALUE(0x1008)) @@ -6533,10 +6530,10 @@ mod hir_opt_tests { PatchPoint NoSingletonClass(Array@0x1020) PatchPoint MethodRedefined(Array@0x1020, zip@0x1028, cme:0x1030) v43:BasicObject = CCallVariadic v36, :zip@0x1058, v39 - v25:BasicObject = GetLocal :result, l0, EP@3 - v29:BasicObject = GetLocal :result, l0, EP@3 + v24:BasicObject = GetLocal :result, l0, EP@3 + PatchPoint NoEPEscape(test) CheckInterrupts - Return v29 + Return v24 "); } @@ -11887,4 +11884,97 @@ mod hir_opt_tests { Return v31 "); } + + #[test] + fn recompile_after_ep_escape_uses_ep_locals() { + // When a method creates a lambda, EP escapes to the heap. After + // invalidation and recompilation, the compiler must use EP-based + // locals (SetLocal/GetLocal) instead of SSA locals, because the + // spill target (stack) and the read target (heap EP) diverge. + eval(" + CONST = {}.freeze + def test_ep_escape(list, sep=nil, iter_method=:each) + sep ||= lambda { } + kwsplat = CONST + list.__send__(iter_method) {|*v| yield(*v) } + end + + test_ep_escape({a: 1}, nil, :each_pair) { |k, v| + test_ep_escape([1], lambda { }) { |x| } + } + test_ep_escape({a: 1}, nil, :each_pair) { |k, v| + test_ep_escape([1], lambda { }) { |x| } + } + "); + assert_snapshot!(hir_string("test_ep_escape"), @r" + fn test_ep_escape@:3: + bb0(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + v2:BasicObject = GetLocal :list, l0, SP@7 + v3:BasicObject = GetLocal :sep, l0, SP@6 + v4:BasicObject = GetLocal :iter_method, l0, SP@5 + v5:NilClass = Const Value(nil) + v6:CPtr = LoadPC + v7:CPtr[CPtr(0x1000)] = Const CPtr(0x1008) + v8:CBool = IsBitEqual v6, v7 + IfTrue v8, bb2(v1, v2, v3, v4, v5) + v10:CPtr[CPtr(0x1000)] = Const CPtr(0x1008) + v11:CBool = IsBitEqual v6, v10 + IfTrue v11, bb4(v1, v2, v3, v4, v5) + Jump bb6(v1, v2, v3, v4, v5) + bb1(v15:BasicObject, v16:BasicObject): + EntryPoint JIT(0) + v17:NilClass = Const Value(nil) + v18:NilClass = Const Value(nil) + v19:NilClass = Const Value(nil) + Jump bb2(v15, v16, v17, v18, v19) + bb2(v35:BasicObject, v36:BasicObject, v37:BasicObject, v38:BasicObject, v39:NilClass): + v42:NilClass = Const Value(nil) + SetLocal :sep, l0, EP@5, v42 + Jump bb4(v35, v36, v42, v38, v39) + bb3(v22:BasicObject, v23:BasicObject, v24:BasicObject): + EntryPoint JIT(1) + v25:NilClass = Const Value(nil) + v26:NilClass = Const Value(nil) + Jump bb4(v22, v23, v24, v25, v26) + bb4(v46:BasicObject, v47:BasicObject, v48:BasicObject, v49:BasicObject, v50:NilClass): + v53:StaticSymbol[:each] = Const Value(VALUE(0x1010)) + SetLocal :iter_method, l0, EP@4, v53 + Jump bb6(v46, v47, v48, v53, v50) + bb5(v29:BasicObject, v30:BasicObject, v31:BasicObject, v32:BasicObject): + EntryPoint JIT(2) + v33:NilClass = Const Value(nil) + Jump bb6(v29, v30, v31, v32, v33) + bb6(v57:BasicObject, v58:BasicObject, v59:BasicObject, v60:BasicObject, v61:NilClass): + CheckInterrupts + v67:CBool = Test v59 + v68:Truthy = RefineType v59, Truthy + IfTrue v67, bb7(v57, v58, v68, v60, v61) + v70:Falsy = RefineType v59, Falsy + PatchPoint NoSingletonClass(Object@0x1018) + PatchPoint MethodRedefined(Object@0x1018, lambda@0x1020, cme:0x1028) + v114:HeapObject[class_exact*:Object@VALUE(0x1018)] = GuardType v57, HeapObject[class_exact*:Object@VALUE(0x1018)] + v115:BasicObject = CCallWithFrame v114, :Kernel#lambda@0x1050, block=0x1058 + v74:BasicObject = GetLocal :list, l0, EP@6 + v76:BasicObject = GetLocal :iter_method, l0, EP@4 + v77:BasicObject = GetLocal :kwsplat, l0, EP@3 + SetLocal :sep, l0, EP@5, v115 + Jump bb7(v57, v74, v115, v76, v77) + bb7(v81:BasicObject, v82:BasicObject, v83:BasicObject, v84:BasicObject, v85:BasicObject): + PatchPoint SingleRactorMode + PatchPoint StableConstantNames(0x1060, CONST) + v110:HashExact[VALUE(0x1068)] = Const Value(VALUE(0x1068)) + SetLocal :kwsplat, l0, EP@3, v110 + v95:BasicObject = GetLocal :list, l0, EP@6 + v97:BasicObject = GetLocal :iter_method, l0, EP@4 + v99:BasicObject = Send v95, 0x1070, :__send__, v97 # SendFallbackReason: Send: unsupported method type Optimized + v100:BasicObject = GetLocal :list, l0, EP@6 + v101:BasicObject = GetLocal :sep, l0, EP@5 + v102:BasicObject = GetLocal :iter_method, l0, EP@4 + v103:BasicObject = GetLocal :kwsplat, l0, EP@3 + CheckInterrupts + Return v99 + "); + } } diff --git a/zjit/src/invariants.rs b/zjit/src/invariants.rs index f1180acf2a2689..7aa13cbfcb4bdd 100644 --- a/zjit/src/invariants.rs +++ b/zjit/src/invariants.rs @@ -206,20 +206,22 @@ pub extern "C" fn rb_zjit_invalidate_no_ep_escape(iseq: IseqPtr) { return; } - // Remember that this ISEQ may escape EP - let invariants = ZJITState::get_invariants(); - invariants.ep_escape_iseqs.insert(iseq); + with_vm_lock(src_loc!(), || { + // Remember that this ISEQ may escape EP + let invariants = ZJITState::get_invariants(); + invariants.ep_escape_iseqs.insert(iseq); - // If the ISEQ has been compiled assuming it doesn't escape EP, invalidate the JIT code. - if let Some(patch_points) = invariants.no_ep_escape_iseq_patch_points.get(&iseq) { - debug!("EP is escaped: {}", iseq_name(iseq)); + // If the ISEQ has been compiled assuming it doesn't escape EP, invalidate the JIT code. + if let Some(patch_points) = invariants.no_ep_escape_iseq_patch_points.get(&iseq) { + debug!("EP is escaped: {}", iseq_name(iseq)); - // Invalidate the patch points for this ISEQ - let cb = ZJITState::get_code_block(); - compile_patch_points!(cb, patch_points, "EP is escaped: {}", iseq_name(iseq)); + // Invalidate the patch points for this ISEQ + let cb = ZJITState::get_code_block(); + compile_patch_points!(cb, patch_points, "EP is escaped: {}", iseq_name(iseq)); - cb.mark_all_executable(); - } + cb.mark_all_executable(); + } + }); } /// Track that JIT code for a ISEQ will assume that base pointer is equal to environment pointer. From e0b757f63aa6e720af6200171b2f0841d4efe7ed Mon Sep 17 00:00:00 2001 From: Nozomi Hijikata <121233810+nozomemein@users.noreply.github.com> Date: Tue, 10 Feb 2026 07:34:59 +0900 Subject: [PATCH 3/9] ZJIT: Compile getconstant insn (#16109) * ZJIT: Compile getconstant insn * ZJIT: add fixme comments for opt_getconstant_path * ZJIT: Address comments - Replace fixme with actionable todo comment - Fix type validation for GetConstant --- test/ruby/test_zjit.rb | 15 +++++++++++++++ zjit/src/codegen.rs | 12 ++++++++++++ zjit/src/hir.rs | 24 ++++++++++++++++++++++++ zjit/src/hir/opt_tests.rs | 27 +++++++++++++++++++++++++++ zjit/src/hir/tests.rs | 26 ++++++++++++++++++++++++++ 5 files changed, 104 insertions(+) diff --git a/test/ruby/test_zjit.rb b/test/ruby/test_zjit.rb index 7b068e9898fc80..095b690c7b3c49 100644 --- a/test/ruby/test_zjit.rb +++ b/test/ruby/test_zjit.rb @@ -3380,6 +3380,21 @@ def test = RUBY_COPYRIGHT }, call_threshold: 1, insns: [:opt_getconstant_path] end + def test_getconstant + assert_compiles '1', %q{ + class Foo + CONST = 1 + end + + def test(klass) + klass::CONST + end + + test(Foo) + test(Foo) + }, call_threshold: 2, insns: [:getconstant] + end + def test_expandarray_no_splat assert_compiles '[3, 4]', %q{ def test(o) diff --git a/zjit/src/codegen.rs b/zjit/src/codegen.rs index e9949705fc8b50..29ea47c68e41e7 100644 --- a/zjit/src/codegen.rs +++ b/zjit/src/codegen.rs @@ -558,6 +558,7 @@ fn gen_insn(cb: &mut CodeBlock, jit: &mut JITState, asm: &mut Assembler, functio &Insn::IsBlockParamModified { level } => gen_is_block_param_modified(asm, level), &Insn::GetBlockParam { ep_offset, level, state } => gen_getblockparam(jit, asm, ep_offset, level, &function.frame_state(state)), &Insn::SetLocal { val, ep_offset, level } => no_output!(gen_setlocal(asm, opnd!(val), function.type_of(val), ep_offset, level)), + Insn::GetConstant { klass, id, allow_nil, state } => gen_getconstant(jit, asm, opnd!(klass), *id, opnd!(allow_nil), &function.frame_state(*state)), Insn::GetConstantPath { ic, state } => gen_get_constant_path(jit, asm, *ic, &function.frame_state(*state)), Insn::GetClassVar { id, ic, state } => gen_getclassvar(jit, asm, *id, *ic, &function.frame_state(*state)), Insn::SetClassVar { id, val, ic, state } => no_output!(gen_setclassvar(jit, asm, *id, opnd!(val), *ic, &function.frame_state(*state))), @@ -811,6 +812,17 @@ fn gen_get_constant_path(jit: &JITState, asm: &mut Assembler, ic: *const iseq_in asm_ccall!(asm, rb_vm_opt_getconstant_path, EC, CFP, Opnd::const_ptr(ic)) } +fn gen_getconstant(jit: &mut JITState, asm: &mut Assembler, klass: Opnd, id: ID, allow_nil: Opnd, state: &FrameState) -> Opnd { + unsafe extern "C" { + fn rb_vm_get_ev_const(ec: EcPtr, klass: VALUE, id: ID, allow_nil: VALUE) -> VALUE; + } + + // Constant lookup can raise and run arbitrary Ruby code via const_missing. + gen_prepare_non_leaf_call(jit, asm, state); + + asm_ccall!(asm, rb_vm_get_ev_const, EC, klass, id.0.into(), allow_nil) +} + fn gen_fixnum_bit_check(asm: &mut Assembler, val: Opnd, index: u8) -> Opnd { let bit_test: u64 = 0x01 << (index + 1); asm.test(val, bit_test.into()); diff --git a/zjit/src/hir.rs b/zjit/src/hir.rs index 3fd5fcc5cbf4ea..fe0122d2973471 100644 --- a/zjit/src/hir.rs +++ b/zjit/src/hir.rs @@ -803,6 +803,7 @@ pub enum Insn { FixnumAref { recv: InsnId, index: InsnId }, // TODO(max): In iseq body types that are not ISEQ_TYPE_METHOD, rewrite to Constant false. Defined { op_type: usize, obj: VALUE, pushval: VALUE, v: InsnId, state: InsnId }, + GetConstant { klass: InsnId, id: ID, allow_nil: InsnId, state: InsnId }, GetConstantPath { ic: *const iseq_inline_constant_cache, state: InsnId }, /// Kernel#block_given? but without pushing a frame. Similar to [`Insn::Defined`] with /// `DEFINED_YIELD` @@ -1139,6 +1140,7 @@ impl Insn { Insn::UnboxFixnum { .. } => effects::Any, Insn::FixnumAref { .. } => effects::Empty, Insn::Defined { .. } => effects::Any, + Insn::GetConstant { .. } => effects::Any, Insn::GetConstantPath { .. } => effects::Any, Insn::IsBlockGiven { .. } => Effect::read_write(abstract_heaps::Other, abstract_heaps::Empty), Insn::FixnumBitCheck { .. } => effects::Any, @@ -1562,6 +1564,9 @@ impl<'a> std::fmt::Display for InsnPrinter<'a> { write!(f, "GetBlockParam {name}l{level}, EP@{ep_offset}") }, Insn::PatchPoint { invariant, .. } => { write!(f, "PatchPoint {}", invariant.print(self.ptr_map)) }, + Insn::GetConstant { klass, id, allow_nil, .. } => { + write!(f, "GetConstant {klass}, :{}, {allow_nil}", id.contents_lossy()) + } Insn::GetConstantPath { ic, .. } => { write!(f, "GetConstantPath {:p}", self.ptr_map.map_ptr(ic)) }, Insn::IsBlockGiven { lep } => { write!(f, "IsBlockGiven {lep}") }, Insn::FixnumBitCheck {val, index} => { write!(f, "FixnumBitCheck {val}, {index}") }, @@ -2356,6 +2361,7 @@ impl Function { }, &Defined { op_type, obj, pushval, v, state } => Defined { op_type, obj, pushval, v: find!(v), state: find!(state) }, &DefinedIvar { self_val, pushval, id, state } => DefinedIvar { self_val: find!(self_val), pushval, id, state }, + &GetConstant { klass, id, allow_nil, state } => GetConstant { klass: find!(klass), id, allow_nil: find!(allow_nil), state }, &NewArray { ref elements, state } => NewArray { elements: find_vec!(elements), state: find!(state) }, &NewHash { ref elements, state } => NewHash { elements: find_vec!(elements), state: find!(state) }, &NewRange { low, high, flag, state } => NewRange { low: find!(low), high: find!(high), flag, state: find!(state) }, @@ -2527,6 +2533,7 @@ impl Function { Insn::InvokeBuiltin { return_type, .. } => return_type.unwrap_or(types::BasicObject), Insn::Defined { pushval, .. } => Type::from_value(*pushval).union(types::NilClass), Insn::DefinedIvar { pushval, .. } => Type::from_value(*pushval).union(types::NilClass), + Insn::GetConstant { .. } => types::BasicObject, Insn::GetConstantPath { .. } => types::BasicObject, Insn::IsBlockGiven { .. } => types::BoolExact, Insn::FixnumBitCheck { .. } => types::BoolExact, @@ -4881,6 +4888,11 @@ impl Function { worklist.push_back(self_val); worklist.push_back(state); } + &Insn::GetConstant { klass, allow_nil, state, .. } => { + worklist.push_back(klass); + worklist.push_back(allow_nil); + worklist.push_back(state); + } &Insn::SetIvar { self_val, val, state, .. } => { worklist.push_back(self_val); worklist.push_back(val); @@ -5507,6 +5519,10 @@ impl Function { self.assert_subtype(insn_id, left, types::BasicObject)?; self.assert_subtype(insn_id, right, types::BasicObject) } + Insn::GetConstant { klass, allow_nil, .. } => { + self.assert_subtype(insn_id, klass, types::BasicObject)?; + self.assert_subtype(insn_id, allow_nil, types::BoolExact) + } // Instructions with recv and a Vec of Ruby objects Insn::SendWithoutBlock { recv, ref args, .. } | Insn::SendDirect { recv, ref args, .. } @@ -6626,8 +6642,16 @@ pub fn iseq_to_hir(iseq: *const rb_iseq_t) -> Result { }; state.stack_push(fun.push_insn(block, Insn::FixnumBitCheck { val, index })); } + YARVINSN_getconstant => { + let id = ID(get_arg(pc, 0).as_u64()); + let allow_nil = state.stack_pop()?; + let klass = state.stack_pop()?; + let result = fun.push_insn(block, Insn::GetConstant { klass, id, allow_nil, state: exit_id }); + state.stack_push(result); + } YARVINSN_opt_getconstant_path => { let ic = get_arg(pc, 0).as_ptr(); + // TODO: Remove this extra Snapshot and pass `exit_id` to `GetConstantPath` instead. let snapshot = fun.push_insn(block, Insn::Snapshot { state: exit_state }); state.stack_push(fun.push_insn(block, Insn::GetConstantPath { ic, state: snapshot })); } diff --git a/zjit/src/hir/opt_tests.rs b/zjit/src/hir/opt_tests.rs index afbbc8bedc39e1..4ce01c438af578 100644 --- a/zjit/src/hir/opt_tests.rs +++ b/zjit/src/hir/opt_tests.rs @@ -2356,6 +2356,33 @@ mod hir_opt_tests { "); } + #[test] + fn test_do_not_eliminate_getconstant() { + eval(" + def test(klass) + klass::ARGV + 5 + end + "); + assert_snapshot!(hir_string("test"), @r" + fn test@:3: + bb0(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + v2:BasicObject = GetLocal :klass, l0, SP@4 + Jump bb2(v1, v2) + bb1(v5:BasicObject, v6:BasicObject): + EntryPoint JIT(0) + Jump bb2(v5, v6) + bb2(v8:BasicObject, v9:BasicObject): + v14:FalseClass = Const Value(false) + v16:BasicObject = GetConstant v9, :ARGV, v14 + v20:Fixnum[5] = Const Value(5) + CheckInterrupts + Return v20 + "); + } + #[test] fn kernel_itself_const() { eval(" diff --git a/zjit/src/hir/tests.rs b/zjit/src/hir/tests.rs index ef1d9597ddacce..f3ab0fa57cfb71 100644 --- a/zjit/src/hir/tests.rs +++ b/zjit/src/hir/tests.rs @@ -2629,6 +2629,32 @@ pub mod hir_build_tests { "); } + #[test] + fn test_getconstant() { + eval(" + def test(klass) + klass::ARGV + end + "); + assert_contains_opcode("test", YARVINSN_getconstant); + assert_snapshot!(hir_string("test"), @r" + fn test@:3: + bb0(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + v2:BasicObject = GetLocal :klass, l0, SP@4 + Jump bb2(v1, v2) + bb1(v5:BasicObject, v6:BasicObject): + EntryPoint JIT(0) + Jump bb2(v5, v6) + bb2(v8:BasicObject, v9:BasicObject): + v14:FalseClass = Const Value(false) + v16:BasicObject = GetConstant v9, :ARGV, v14 + CheckInterrupts + Return v16 + "); + } + #[test] fn test_getinstancevariable() { eval(" From 836c1700104c305fa13cbc7b17d114705dd807b2 Mon Sep 17 00:00:00 2001 From: Mike Dalessio Date: Mon, 9 Feb 2026 13:45:34 -0500 Subject: [PATCH 4/9] Fix UnboundMethod#== for methods from included/extended modules Method#unbind clones the method entry, preserving its defined_class. For methods mixed in via include/extend, defined_class is an ICLASS, causing UnboundMethod#== to return false when comparing against the same method obtained via Module#instance_method. Resolve ICLASS defined_class in method_eq. Fixes [Bug #21873] --- proc.c | 2 ++ test/ruby/test_method.rb | 14 ++++++++++++++ 2 files changed, 16 insertions(+) diff --git a/proc.c b/proc.c index f0cdcae7b1da7f..67963a1b011f9c 100644 --- a/proc.c +++ b/proc.c @@ -2006,6 +2006,8 @@ method_eq(VALUE method, VALUE other) klass1 = method_entry_defined_class(m1->me); klass2 = method_entry_defined_class(m2->me); + if (RB_TYPE_P(klass1, T_ICLASS)) klass1 = RBASIC_CLASS(klass1); + if (RB_TYPE_P(klass2, T_ICLASS)) klass2 = RBASIC_CLASS(klass2); if (!rb_method_entry_eq(m1->me, m2->me) || klass1 != klass2 || diff --git a/test/ruby/test_method.rb b/test/ruby/test_method.rb index 8561f841a8cda6..c3819cdebf7850 100644 --- a/test/ruby/test_method.rb +++ b/test/ruby/test_method.rb @@ -111,6 +111,20 @@ def foo() :derived; end end end + def test_unbound_method_equality_with_extended_module + m = Module.new { def hello; "hello"; end } + base = Class.new { extend m } + sub = Class.new(base) + + from_module = m.instance_method(:hello) + from_base = base.method(:hello).unbind + from_sub = sub.method(:hello).unbind + + assert_equal(from_module, from_base) + assert_equal(from_module, from_sub) + assert_equal(from_base, from_sub) + end + def test_callee assert_equal(:test_callee, __method__) assert_equal(:m, Class.new {def m; __method__; end}.new.m) From f43d294d58f96227b33b491fcbd347dd605d3c67 Mon Sep 17 00:00:00 2001 From: Max Bernstein Date: Fri, 6 Feb 2026 15:50:56 -0500 Subject: [PATCH 5/9] Add without_interrupts primitive attribute to skip interrupt checks --- array.rb | 8 +- compile.c | 20 ++ insns.def | 50 +++++ prism_compile.c | 3 + tool/mk_builtin_loader.rb | 2 +- vm_core.h | 2 + yjit/src/codegen.rs | 12 +- yjit/src/cruby_bindings.inc.rs | 359 +++++++++++++++++---------------- zjit/src/cruby_bindings.inc.rs | 359 +++++++++++++++++---------------- zjit/src/hir.rs | 31 ++- 10 files changed, 478 insertions(+), 368 deletions(-) diff --git a/array.rb b/array.rb index f45e6a0d5e5ade..4fee5ecaebe7c9 100644 --- a/array.rb +++ b/array.rb @@ -217,7 +217,7 @@ def fetch_values(*indexes, &block) undef :each def each # :nodoc: - Primitive.attr! :inline_block, :c_trace + Primitive.attr! :inline_block, :c_trace, :without_interrupts unless defined?(yield) return Primitive.cexpr! 'SIZED_ENUMERATOR(self, 0, 0, ary_enum_length)' @@ -235,7 +235,7 @@ def each # :nodoc: undef :map def map # :nodoc: - Primitive.attr! :inline_block, :c_trace + Primitive.attr! :inline_block, :c_trace, :without_interrupts unless defined?(yield) return Primitive.cexpr! 'SIZED_ENUMERATOR(self, 0, 0, ary_enum_length)' @@ -261,7 +261,7 @@ def map # :nodoc: undef :select def select # :nodoc: - Primitive.attr! :inline_block, :c_trace + Primitive.attr! :inline_block, :c_trace, :without_interrupts unless defined?(yield) return Primitive.cexpr! 'SIZED_ENUMERATOR(self, 0, 0, ary_enum_length)' @@ -288,7 +288,7 @@ def select # :nodoc: undef :find def find(if_none_proc = nil) # :nodoc: - Primitive.attr! :inline_block, :c_trace + Primitive.attr! :inline_block, :c_trace, :without_interrupts unless defined?(yield) return Primitive.cexpr! 'SIZED_ENUMERATOR(self, 0, 0, ary_enum_length)' diff --git a/compile.c b/compile.c index a5a0ab46e9b8bc..5d6cfb1e1690c5 100644 --- a/compile.c +++ b/compile.c @@ -4433,6 +4433,7 @@ iseq_optimize(rb_iseq_t *iseq, LINK_ANCHOR *const anchor) ISEQ_COMPILE_DATA(iseq)->option->tailcall_optimization; const int do_si = ISEQ_COMPILE_DATA(iseq)->option->specialized_instruction; const int do_ou = ISEQ_COMPILE_DATA(iseq)->option->operands_unification; + const int do_without_ints = ISEQ_BODY(iseq)->builtin_attrs & BUILTIN_ATTR_WITHOUT_INTERRUPTS; int rescue_level = 0; int tailcallopt = do_tailcallopt; @@ -4465,6 +4466,22 @@ iseq_optimize(rb_iseq_t *iseq, LINK_ANCHOR *const anchor) insn_operands_unification((INSN *)list); } + if (do_without_ints) { + INSN *item = (INSN *)list; + if (IS_INSN_ID(item, jump)) { + item->insn_id = BIN(jump_without_ints); + } + else if (IS_INSN_ID(item, branchif)) { + item->insn_id = BIN(branchif_without_ints); + } + else if (IS_INSN_ID(item, branchunless)) { + item->insn_id = BIN(branchunless_without_ints); + } + else if (IS_INSN_ID(item, branchnil)) { + item->insn_id = BIN(branchnil_without_ints); + } + } + if (do_block_optimization) { INSN * item = (INSN *)list; // Give up if there is a throw @@ -9223,6 +9240,9 @@ compile_builtin_attr(rb_iseq_t *iseq, const NODE *node) // Let the iseq act like a C method in backtraces ISEQ_BODY(iseq)->builtin_attrs |= BUILTIN_ATTR_C_TRACE; } + else if (strcmp(RSTRING_PTR(string), "without_interrupts") == 0) { + ISEQ_BODY(iseq)->builtin_attrs |= BUILTIN_ATTR_WITHOUT_INTERRUPTS; + } else { goto unknown_arg; } diff --git a/insns.def b/insns.def index f9a334d824b31a..b7a60f3d5d3a2a 100644 --- a/insns.def +++ b/insns.def @@ -1265,6 +1265,56 @@ branchnil } } +/* same as jump, but without interrupt check */ +DEFINE_INSN +jump_without_ints +(OFFSET dst) +() +() +// attr bool leaf = true; +{ + JUMP(dst); +} + +/* same as branchif, but without interrupt check */ +DEFINE_INSN +branchif_without_ints +(OFFSET dst) +(VALUE val) +() +// attr bool leaf = true; +{ + if (RTEST(val)) { + JUMP(dst); + } +} + +/* same as branchunless, but without interrupt check */ +DEFINE_INSN +branchunless_without_ints +(OFFSET dst) +(VALUE val) +() +// attr bool leaf = true; +{ + if (!RTEST(val)) { + JUMP(dst); + } +} + +/* same as branchnil, but without interrupt check */ +DEFINE_INSN +branchnil_without_ints +(OFFSET dst) +(VALUE val) +() +// attr bool leaf = true; +{ + if (NIL_P(val)) { + JUMP(dst); + } +} + /**********************************************************/ /* for optimize */ /**********************************************************/ diff --git a/prism_compile.c b/prism_compile.c index 731859b35aee80..5d83e55a776136 100644 --- a/prism_compile.c +++ b/prism_compile.c @@ -3441,6 +3441,9 @@ pm_compile_builtin_attr(rb_iseq_t *iseq, const pm_scope_node_t *scope_node, cons // Let the iseq act like a C method in backtraces ISEQ_BODY(iseq)->builtin_attrs |= BUILTIN_ATTR_C_TRACE; } + else if (strcmp(RSTRING_PTR(string), "without_interrupts") == 0) { + ISEQ_BODY(iseq)->builtin_attrs |= BUILTIN_ATTR_WITHOUT_INTERRUPTS; + } else { COMPILE_ERROR(iseq, node_location->line, "unknown argument to attr!: %s", RSTRING_PTR(string)); return COMPILE_NG; diff --git a/tool/mk_builtin_loader.rb b/tool/mk_builtin_loader.rb index 5aa07962f93b4b..3ab36ec45c323d 100644 --- a/tool/mk_builtin_loader.rb +++ b/tool/mk_builtin_loader.rb @@ -6,7 +6,7 @@ SUBLIBS = {} REQUIRED = {} -BUILTIN_ATTRS = %w[leaf inline_block use_block c_trace] +BUILTIN_ATTRS = %w[leaf inline_block use_block c_trace without_interrupts] module CompileWarning @@warnings = 0 diff --git a/vm_core.h b/vm_core.h index b700a32909d6c7..5f0266da1da516 100644 --- a/vm_core.h +++ b/vm_core.h @@ -403,6 +403,8 @@ enum rb_builtin_attr { BUILTIN_ATTR_INLINE_BLOCK = 0x04, // The iseq acts like a C method in backtraces. BUILTIN_ATTR_C_TRACE = 0x08, + // The iseq uses noint branch/jump opcodes that skip interrupt checking. + BUILTIN_ATTR_WITHOUT_INTERRUPTS = 0x10, }; typedef VALUE (*rb_jit_func_t)(struct rb_execution_context_struct *, struct rb_control_frame_struct *); diff --git a/yjit/src/codegen.rs b/yjit/src/codegen.rs index 0fbca85716dff0..0a4832d0949b56 100644 --- a/yjit/src/codegen.rs +++ b/yjit/src/codegen.rs @@ -4641,7 +4641,7 @@ fn gen_branchif( let jump_offset = jit.get_arg(0).as_i32(); // Check for interrupts, but only on backward branches that may create loops - if jump_offset < 0 { + if jump_offset < 0 && jit.get_opcode() != YARVINSN_branchif_without_ints as usize { gen_check_ints(asm, Counter::branchif_interrupted); } @@ -4693,7 +4693,7 @@ fn gen_branchunless( let jump_offset = jit.get_arg(0).as_i32(); // Check for interrupts, but only on backward branches that may create loops - if jump_offset < 0 { + if jump_offset < 0 && jit.get_opcode() != YARVINSN_branchunless_without_ints as usize { gen_check_ints(asm, Counter::branchunless_interrupted); } @@ -4746,7 +4746,7 @@ fn gen_branchnil( let jump_offset = jit.get_arg(0).as_i32(); // Check for interrupts, but only on backward branches that may create loops - if jump_offset < 0 { + if jump_offset < 0 && jit.get_opcode() != YARVINSN_branchnil_without_ints as usize { gen_check_ints(asm, Counter::branchnil_interrupted); } @@ -4901,7 +4901,7 @@ fn gen_jump( let jump_offset = jit.get_arg(0).as_i32(); // Check for interrupts, but only on backward branches that may create loops - if jump_offset < 0 { + if jump_offset < 0 && jit.get_opcode() != YARVINSN_jump_without_ints as usize { gen_check_ints(asm, Counter::jump_interrupted); } @@ -10777,6 +10777,10 @@ fn get_gen_fn(opcode: VALUE) -> Option { YARVINSN_branchnil => Some(gen_branchnil), YARVINSN_throw => Some(gen_throw), YARVINSN_jump => Some(gen_jump), + YARVINSN_branchif_without_ints => Some(gen_branchif), + YARVINSN_branchunless_without_ints => Some(gen_branchunless), + YARVINSN_branchnil_without_ints => Some(gen_branchnil), + YARVINSN_jump_without_ints => Some(gen_jump), YARVINSN_opt_new => Some(gen_opt_new), YARVINSN_getblockparamproxy => Some(gen_getblockparamproxy), diff --git a/yjit/src/cruby_bindings.inc.rs b/yjit/src/cruby_bindings.inc.rs index 57824f49118358..9ce8b5a020042b 100644 --- a/yjit/src/cruby_bindings.inc.rs +++ b/yjit/src/cruby_bindings.inc.rs @@ -486,6 +486,7 @@ pub const BUILTIN_ATTR_LEAF: rb_builtin_attr = 1; pub const BUILTIN_ATTR_SINGLE_NOARG_LEAF: rb_builtin_attr = 2; pub const BUILTIN_ATTR_INLINE_BLOCK: rb_builtin_attr = 4; pub const BUILTIN_ATTR_C_TRACE: rb_builtin_attr = 8; +pub const BUILTIN_ATTR_WITHOUT_INTERRUPTS: rb_builtin_attr = 16; pub type rb_builtin_attr = u32; #[repr(C)] #[derive(Debug, Copy, Clone)] @@ -775,181 +776,189 @@ pub const YARVINSN_jump: ruby_vminsn_type = 72; pub const YARVINSN_branchif: ruby_vminsn_type = 73; pub const YARVINSN_branchunless: ruby_vminsn_type = 74; pub const YARVINSN_branchnil: ruby_vminsn_type = 75; -pub const YARVINSN_once: ruby_vminsn_type = 76; -pub const YARVINSN_opt_case_dispatch: ruby_vminsn_type = 77; -pub const YARVINSN_opt_plus: ruby_vminsn_type = 78; -pub const YARVINSN_opt_minus: ruby_vminsn_type = 79; -pub const YARVINSN_opt_mult: ruby_vminsn_type = 80; -pub const YARVINSN_opt_div: ruby_vminsn_type = 81; -pub const YARVINSN_opt_mod: ruby_vminsn_type = 82; -pub const YARVINSN_opt_eq: ruby_vminsn_type = 83; -pub const YARVINSN_opt_neq: ruby_vminsn_type = 84; -pub const YARVINSN_opt_lt: ruby_vminsn_type = 85; -pub const YARVINSN_opt_le: ruby_vminsn_type = 86; -pub const YARVINSN_opt_gt: ruby_vminsn_type = 87; -pub const YARVINSN_opt_ge: ruby_vminsn_type = 88; -pub const YARVINSN_opt_ltlt: ruby_vminsn_type = 89; -pub const YARVINSN_opt_and: ruby_vminsn_type = 90; -pub const YARVINSN_opt_or: ruby_vminsn_type = 91; -pub const YARVINSN_opt_aref: ruby_vminsn_type = 92; -pub const YARVINSN_opt_aset: ruby_vminsn_type = 93; -pub const YARVINSN_opt_length: ruby_vminsn_type = 94; -pub const YARVINSN_opt_size: ruby_vminsn_type = 95; -pub const YARVINSN_opt_empty_p: ruby_vminsn_type = 96; -pub const YARVINSN_opt_succ: ruby_vminsn_type = 97; -pub const YARVINSN_opt_not: ruby_vminsn_type = 98; -pub const YARVINSN_opt_regexpmatch2: ruby_vminsn_type = 99; -pub const YARVINSN_invokebuiltin: ruby_vminsn_type = 100; -pub const YARVINSN_opt_invokebuiltin_delegate: ruby_vminsn_type = 101; -pub const YARVINSN_opt_invokebuiltin_delegate_leave: ruby_vminsn_type = 102; -pub const YARVINSN_getlocal_WC_0: ruby_vminsn_type = 103; -pub const YARVINSN_getlocal_WC_1: ruby_vminsn_type = 104; -pub const YARVINSN_setlocal_WC_0: ruby_vminsn_type = 105; -pub const YARVINSN_setlocal_WC_1: ruby_vminsn_type = 106; -pub const YARVINSN_putobject_INT2FIX_0_: ruby_vminsn_type = 107; -pub const YARVINSN_putobject_INT2FIX_1_: ruby_vminsn_type = 108; -pub const YARVINSN_trace_nop: ruby_vminsn_type = 109; -pub const YARVINSN_trace_getlocal: ruby_vminsn_type = 110; -pub const YARVINSN_trace_setlocal: ruby_vminsn_type = 111; -pub const YARVINSN_trace_getblockparam: ruby_vminsn_type = 112; -pub const YARVINSN_trace_setblockparam: ruby_vminsn_type = 113; -pub const YARVINSN_trace_getblockparamproxy: ruby_vminsn_type = 114; -pub const YARVINSN_trace_getspecial: ruby_vminsn_type = 115; -pub const YARVINSN_trace_setspecial: ruby_vminsn_type = 116; -pub const YARVINSN_trace_getinstancevariable: ruby_vminsn_type = 117; -pub const YARVINSN_trace_setinstancevariable: ruby_vminsn_type = 118; -pub const YARVINSN_trace_getclassvariable: ruby_vminsn_type = 119; -pub const YARVINSN_trace_setclassvariable: ruby_vminsn_type = 120; -pub const YARVINSN_trace_opt_getconstant_path: ruby_vminsn_type = 121; -pub const YARVINSN_trace_getconstant: ruby_vminsn_type = 122; -pub const YARVINSN_trace_setconstant: ruby_vminsn_type = 123; -pub const YARVINSN_trace_getglobal: ruby_vminsn_type = 124; -pub const YARVINSN_trace_setglobal: ruby_vminsn_type = 125; -pub const YARVINSN_trace_putnil: ruby_vminsn_type = 126; -pub const YARVINSN_trace_putself: ruby_vminsn_type = 127; -pub const YARVINSN_trace_putobject: ruby_vminsn_type = 128; -pub const YARVINSN_trace_putspecialobject: ruby_vminsn_type = 129; -pub const YARVINSN_trace_putstring: ruby_vminsn_type = 130; -pub const YARVINSN_trace_putchilledstring: ruby_vminsn_type = 131; -pub const YARVINSN_trace_concatstrings: ruby_vminsn_type = 132; -pub const YARVINSN_trace_anytostring: ruby_vminsn_type = 133; -pub const YARVINSN_trace_toregexp: ruby_vminsn_type = 134; -pub const YARVINSN_trace_intern: ruby_vminsn_type = 135; -pub const YARVINSN_trace_newarray: ruby_vminsn_type = 136; -pub const YARVINSN_trace_pushtoarraykwsplat: ruby_vminsn_type = 137; -pub const YARVINSN_trace_duparray: ruby_vminsn_type = 138; -pub const YARVINSN_trace_duphash: ruby_vminsn_type = 139; -pub const YARVINSN_trace_expandarray: ruby_vminsn_type = 140; -pub const YARVINSN_trace_concatarray: ruby_vminsn_type = 141; -pub const YARVINSN_trace_concattoarray: ruby_vminsn_type = 142; -pub const YARVINSN_trace_pushtoarray: ruby_vminsn_type = 143; -pub const YARVINSN_trace_splatarray: ruby_vminsn_type = 144; -pub const YARVINSN_trace_splatkw: ruby_vminsn_type = 145; -pub const YARVINSN_trace_newhash: ruby_vminsn_type = 146; -pub const YARVINSN_trace_newrange: ruby_vminsn_type = 147; -pub const YARVINSN_trace_pop: ruby_vminsn_type = 148; -pub const YARVINSN_trace_dup: ruby_vminsn_type = 149; -pub const YARVINSN_trace_dupn: ruby_vminsn_type = 150; -pub const YARVINSN_trace_swap: ruby_vminsn_type = 151; -pub const YARVINSN_trace_opt_reverse: ruby_vminsn_type = 152; -pub const YARVINSN_trace_topn: ruby_vminsn_type = 153; -pub const YARVINSN_trace_setn: ruby_vminsn_type = 154; -pub const YARVINSN_trace_adjuststack: ruby_vminsn_type = 155; -pub const YARVINSN_trace_defined: ruby_vminsn_type = 156; -pub const YARVINSN_trace_definedivar: ruby_vminsn_type = 157; -pub const YARVINSN_trace_checkmatch: ruby_vminsn_type = 158; -pub const YARVINSN_trace_checkkeyword: ruby_vminsn_type = 159; -pub const YARVINSN_trace_checktype: ruby_vminsn_type = 160; -pub const YARVINSN_trace_defineclass: ruby_vminsn_type = 161; -pub const YARVINSN_trace_definemethod: ruby_vminsn_type = 162; -pub const YARVINSN_trace_definesmethod: ruby_vminsn_type = 163; -pub const YARVINSN_trace_send: ruby_vminsn_type = 164; -pub const YARVINSN_trace_sendforward: ruby_vminsn_type = 165; -pub const YARVINSN_trace_opt_send_without_block: ruby_vminsn_type = 166; -pub const YARVINSN_trace_opt_new: ruby_vminsn_type = 167; -pub const YARVINSN_trace_objtostring: ruby_vminsn_type = 168; -pub const YARVINSN_trace_opt_ary_freeze: ruby_vminsn_type = 169; -pub const YARVINSN_trace_opt_hash_freeze: ruby_vminsn_type = 170; -pub const YARVINSN_trace_opt_str_freeze: ruby_vminsn_type = 171; -pub const YARVINSN_trace_opt_nil_p: ruby_vminsn_type = 172; -pub const YARVINSN_trace_opt_str_uminus: ruby_vminsn_type = 173; -pub const YARVINSN_trace_opt_duparray_send: ruby_vminsn_type = 174; -pub const YARVINSN_trace_opt_newarray_send: ruby_vminsn_type = 175; -pub const YARVINSN_trace_invokesuper: ruby_vminsn_type = 176; -pub const YARVINSN_trace_invokesuperforward: ruby_vminsn_type = 177; -pub const YARVINSN_trace_invokeblock: ruby_vminsn_type = 178; -pub const YARVINSN_trace_leave: ruby_vminsn_type = 179; -pub const YARVINSN_trace_throw: ruby_vminsn_type = 180; -pub const YARVINSN_trace_jump: ruby_vminsn_type = 181; -pub const YARVINSN_trace_branchif: ruby_vminsn_type = 182; -pub const YARVINSN_trace_branchunless: ruby_vminsn_type = 183; -pub const YARVINSN_trace_branchnil: ruby_vminsn_type = 184; -pub const YARVINSN_trace_once: ruby_vminsn_type = 185; -pub const YARVINSN_trace_opt_case_dispatch: ruby_vminsn_type = 186; -pub const YARVINSN_trace_opt_plus: ruby_vminsn_type = 187; -pub const YARVINSN_trace_opt_minus: ruby_vminsn_type = 188; -pub const YARVINSN_trace_opt_mult: ruby_vminsn_type = 189; -pub const YARVINSN_trace_opt_div: ruby_vminsn_type = 190; -pub const YARVINSN_trace_opt_mod: ruby_vminsn_type = 191; -pub const YARVINSN_trace_opt_eq: ruby_vminsn_type = 192; -pub const YARVINSN_trace_opt_neq: ruby_vminsn_type = 193; -pub const YARVINSN_trace_opt_lt: ruby_vminsn_type = 194; -pub const YARVINSN_trace_opt_le: ruby_vminsn_type = 195; -pub const YARVINSN_trace_opt_gt: ruby_vminsn_type = 196; -pub const YARVINSN_trace_opt_ge: ruby_vminsn_type = 197; -pub const YARVINSN_trace_opt_ltlt: ruby_vminsn_type = 198; -pub const YARVINSN_trace_opt_and: ruby_vminsn_type = 199; -pub const YARVINSN_trace_opt_or: ruby_vminsn_type = 200; -pub const YARVINSN_trace_opt_aref: ruby_vminsn_type = 201; -pub const YARVINSN_trace_opt_aset: ruby_vminsn_type = 202; -pub const YARVINSN_trace_opt_length: ruby_vminsn_type = 203; -pub const YARVINSN_trace_opt_size: ruby_vminsn_type = 204; -pub const YARVINSN_trace_opt_empty_p: ruby_vminsn_type = 205; -pub const YARVINSN_trace_opt_succ: ruby_vminsn_type = 206; -pub const YARVINSN_trace_opt_not: ruby_vminsn_type = 207; -pub const YARVINSN_trace_opt_regexpmatch2: ruby_vminsn_type = 208; -pub const YARVINSN_trace_invokebuiltin: ruby_vminsn_type = 209; -pub const YARVINSN_trace_opt_invokebuiltin_delegate: ruby_vminsn_type = 210; -pub const YARVINSN_trace_opt_invokebuiltin_delegate_leave: ruby_vminsn_type = 211; -pub const YARVINSN_trace_getlocal_WC_0: ruby_vminsn_type = 212; -pub const YARVINSN_trace_getlocal_WC_1: ruby_vminsn_type = 213; -pub const YARVINSN_trace_setlocal_WC_0: ruby_vminsn_type = 214; -pub const YARVINSN_trace_setlocal_WC_1: ruby_vminsn_type = 215; -pub const YARVINSN_trace_putobject_INT2FIX_0_: ruby_vminsn_type = 216; -pub const YARVINSN_trace_putobject_INT2FIX_1_: ruby_vminsn_type = 217; -pub const YARVINSN_zjit_getblockparamproxy: ruby_vminsn_type = 218; -pub const YARVINSN_zjit_getinstancevariable: ruby_vminsn_type = 219; -pub const YARVINSN_zjit_setinstancevariable: ruby_vminsn_type = 220; -pub const YARVINSN_zjit_definedivar: ruby_vminsn_type = 221; -pub const YARVINSN_zjit_send: ruby_vminsn_type = 222; -pub const YARVINSN_zjit_opt_send_without_block: ruby_vminsn_type = 223; -pub const YARVINSN_zjit_objtostring: ruby_vminsn_type = 224; -pub const YARVINSN_zjit_opt_nil_p: ruby_vminsn_type = 225; -pub const YARVINSN_zjit_invokesuper: ruby_vminsn_type = 226; -pub const YARVINSN_zjit_invokeblock: ruby_vminsn_type = 227; -pub const YARVINSN_zjit_opt_plus: ruby_vminsn_type = 228; -pub const YARVINSN_zjit_opt_minus: ruby_vminsn_type = 229; -pub const YARVINSN_zjit_opt_mult: ruby_vminsn_type = 230; -pub const YARVINSN_zjit_opt_div: ruby_vminsn_type = 231; -pub const YARVINSN_zjit_opt_mod: ruby_vminsn_type = 232; -pub const YARVINSN_zjit_opt_eq: ruby_vminsn_type = 233; -pub const YARVINSN_zjit_opt_neq: ruby_vminsn_type = 234; -pub const YARVINSN_zjit_opt_lt: ruby_vminsn_type = 235; -pub const YARVINSN_zjit_opt_le: ruby_vminsn_type = 236; -pub const YARVINSN_zjit_opt_gt: ruby_vminsn_type = 237; -pub const YARVINSN_zjit_opt_ge: ruby_vminsn_type = 238; -pub const YARVINSN_zjit_opt_ltlt: ruby_vminsn_type = 239; -pub const YARVINSN_zjit_opt_and: ruby_vminsn_type = 240; -pub const YARVINSN_zjit_opt_or: ruby_vminsn_type = 241; -pub const YARVINSN_zjit_opt_aref: ruby_vminsn_type = 242; -pub const YARVINSN_zjit_opt_aset: ruby_vminsn_type = 243; -pub const YARVINSN_zjit_opt_length: ruby_vminsn_type = 244; -pub const YARVINSN_zjit_opt_size: ruby_vminsn_type = 245; -pub const YARVINSN_zjit_opt_empty_p: ruby_vminsn_type = 246; -pub const YARVINSN_zjit_opt_succ: ruby_vminsn_type = 247; -pub const YARVINSN_zjit_opt_not: ruby_vminsn_type = 248; -pub const YARVINSN_zjit_opt_regexpmatch2: ruby_vminsn_type = 249; -pub const VM_INSTRUCTION_SIZE: ruby_vminsn_type = 250; +pub const YARVINSN_jump_without_ints: ruby_vminsn_type = 76; +pub const YARVINSN_branchif_without_ints: ruby_vminsn_type = 77; +pub const YARVINSN_branchunless_without_ints: ruby_vminsn_type = 78; +pub const YARVINSN_branchnil_without_ints: ruby_vminsn_type = 79; +pub const YARVINSN_once: ruby_vminsn_type = 80; +pub const YARVINSN_opt_case_dispatch: ruby_vminsn_type = 81; +pub const YARVINSN_opt_plus: ruby_vminsn_type = 82; +pub const YARVINSN_opt_minus: ruby_vminsn_type = 83; +pub const YARVINSN_opt_mult: ruby_vminsn_type = 84; +pub const YARVINSN_opt_div: ruby_vminsn_type = 85; +pub const YARVINSN_opt_mod: ruby_vminsn_type = 86; +pub const YARVINSN_opt_eq: ruby_vminsn_type = 87; +pub const YARVINSN_opt_neq: ruby_vminsn_type = 88; +pub const YARVINSN_opt_lt: ruby_vminsn_type = 89; +pub const YARVINSN_opt_le: ruby_vminsn_type = 90; +pub const YARVINSN_opt_gt: ruby_vminsn_type = 91; +pub const YARVINSN_opt_ge: ruby_vminsn_type = 92; +pub const YARVINSN_opt_ltlt: ruby_vminsn_type = 93; +pub const YARVINSN_opt_and: ruby_vminsn_type = 94; +pub const YARVINSN_opt_or: ruby_vminsn_type = 95; +pub const YARVINSN_opt_aref: ruby_vminsn_type = 96; +pub const YARVINSN_opt_aset: ruby_vminsn_type = 97; +pub const YARVINSN_opt_length: ruby_vminsn_type = 98; +pub const YARVINSN_opt_size: ruby_vminsn_type = 99; +pub const YARVINSN_opt_empty_p: ruby_vminsn_type = 100; +pub const YARVINSN_opt_succ: ruby_vminsn_type = 101; +pub const YARVINSN_opt_not: ruby_vminsn_type = 102; +pub const YARVINSN_opt_regexpmatch2: ruby_vminsn_type = 103; +pub const YARVINSN_invokebuiltin: ruby_vminsn_type = 104; +pub const YARVINSN_opt_invokebuiltin_delegate: ruby_vminsn_type = 105; +pub const YARVINSN_opt_invokebuiltin_delegate_leave: ruby_vminsn_type = 106; +pub const YARVINSN_getlocal_WC_0: ruby_vminsn_type = 107; +pub const YARVINSN_getlocal_WC_1: ruby_vminsn_type = 108; +pub const YARVINSN_setlocal_WC_0: ruby_vminsn_type = 109; +pub const YARVINSN_setlocal_WC_1: ruby_vminsn_type = 110; +pub const YARVINSN_putobject_INT2FIX_0_: ruby_vminsn_type = 111; +pub const YARVINSN_putobject_INT2FIX_1_: ruby_vminsn_type = 112; +pub const YARVINSN_trace_nop: ruby_vminsn_type = 113; +pub const YARVINSN_trace_getlocal: ruby_vminsn_type = 114; +pub const YARVINSN_trace_setlocal: ruby_vminsn_type = 115; +pub const YARVINSN_trace_getblockparam: ruby_vminsn_type = 116; +pub const YARVINSN_trace_setblockparam: ruby_vminsn_type = 117; +pub const YARVINSN_trace_getblockparamproxy: ruby_vminsn_type = 118; +pub const YARVINSN_trace_getspecial: ruby_vminsn_type = 119; +pub const YARVINSN_trace_setspecial: ruby_vminsn_type = 120; +pub const YARVINSN_trace_getinstancevariable: ruby_vminsn_type = 121; +pub const YARVINSN_trace_setinstancevariable: ruby_vminsn_type = 122; +pub const YARVINSN_trace_getclassvariable: ruby_vminsn_type = 123; +pub const YARVINSN_trace_setclassvariable: ruby_vminsn_type = 124; +pub const YARVINSN_trace_opt_getconstant_path: ruby_vminsn_type = 125; +pub const YARVINSN_trace_getconstant: ruby_vminsn_type = 126; +pub const YARVINSN_trace_setconstant: ruby_vminsn_type = 127; +pub const YARVINSN_trace_getglobal: ruby_vminsn_type = 128; +pub const YARVINSN_trace_setglobal: ruby_vminsn_type = 129; +pub const YARVINSN_trace_putnil: ruby_vminsn_type = 130; +pub const YARVINSN_trace_putself: ruby_vminsn_type = 131; +pub const YARVINSN_trace_putobject: ruby_vminsn_type = 132; +pub const YARVINSN_trace_putspecialobject: ruby_vminsn_type = 133; +pub const YARVINSN_trace_putstring: ruby_vminsn_type = 134; +pub const YARVINSN_trace_putchilledstring: ruby_vminsn_type = 135; +pub const YARVINSN_trace_concatstrings: ruby_vminsn_type = 136; +pub const YARVINSN_trace_anytostring: ruby_vminsn_type = 137; +pub const YARVINSN_trace_toregexp: ruby_vminsn_type = 138; +pub const YARVINSN_trace_intern: ruby_vminsn_type = 139; +pub const YARVINSN_trace_newarray: ruby_vminsn_type = 140; +pub const YARVINSN_trace_pushtoarraykwsplat: ruby_vminsn_type = 141; +pub const YARVINSN_trace_duparray: ruby_vminsn_type = 142; +pub const YARVINSN_trace_duphash: ruby_vminsn_type = 143; +pub const YARVINSN_trace_expandarray: ruby_vminsn_type = 144; +pub const YARVINSN_trace_concatarray: ruby_vminsn_type = 145; +pub const YARVINSN_trace_concattoarray: ruby_vminsn_type = 146; +pub const YARVINSN_trace_pushtoarray: ruby_vminsn_type = 147; +pub const YARVINSN_trace_splatarray: ruby_vminsn_type = 148; +pub const YARVINSN_trace_splatkw: ruby_vminsn_type = 149; +pub const YARVINSN_trace_newhash: ruby_vminsn_type = 150; +pub const YARVINSN_trace_newrange: ruby_vminsn_type = 151; +pub const YARVINSN_trace_pop: ruby_vminsn_type = 152; +pub const YARVINSN_trace_dup: ruby_vminsn_type = 153; +pub const YARVINSN_trace_dupn: ruby_vminsn_type = 154; +pub const YARVINSN_trace_swap: ruby_vminsn_type = 155; +pub const YARVINSN_trace_opt_reverse: ruby_vminsn_type = 156; +pub const YARVINSN_trace_topn: ruby_vminsn_type = 157; +pub const YARVINSN_trace_setn: ruby_vminsn_type = 158; +pub const YARVINSN_trace_adjuststack: ruby_vminsn_type = 159; +pub const YARVINSN_trace_defined: ruby_vminsn_type = 160; +pub const YARVINSN_trace_definedivar: ruby_vminsn_type = 161; +pub const YARVINSN_trace_checkmatch: ruby_vminsn_type = 162; +pub const YARVINSN_trace_checkkeyword: ruby_vminsn_type = 163; +pub const YARVINSN_trace_checktype: ruby_vminsn_type = 164; +pub const YARVINSN_trace_defineclass: ruby_vminsn_type = 165; +pub const YARVINSN_trace_definemethod: ruby_vminsn_type = 166; +pub const YARVINSN_trace_definesmethod: ruby_vminsn_type = 167; +pub const YARVINSN_trace_send: ruby_vminsn_type = 168; +pub const YARVINSN_trace_sendforward: ruby_vminsn_type = 169; +pub const YARVINSN_trace_opt_send_without_block: ruby_vminsn_type = 170; +pub const YARVINSN_trace_opt_new: ruby_vminsn_type = 171; +pub const YARVINSN_trace_objtostring: ruby_vminsn_type = 172; +pub const YARVINSN_trace_opt_ary_freeze: ruby_vminsn_type = 173; +pub const YARVINSN_trace_opt_hash_freeze: ruby_vminsn_type = 174; +pub const YARVINSN_trace_opt_str_freeze: ruby_vminsn_type = 175; +pub const YARVINSN_trace_opt_nil_p: ruby_vminsn_type = 176; +pub const YARVINSN_trace_opt_str_uminus: ruby_vminsn_type = 177; +pub const YARVINSN_trace_opt_duparray_send: ruby_vminsn_type = 178; +pub const YARVINSN_trace_opt_newarray_send: ruby_vminsn_type = 179; +pub const YARVINSN_trace_invokesuper: ruby_vminsn_type = 180; +pub const YARVINSN_trace_invokesuperforward: ruby_vminsn_type = 181; +pub const YARVINSN_trace_invokeblock: ruby_vminsn_type = 182; +pub const YARVINSN_trace_leave: ruby_vminsn_type = 183; +pub const YARVINSN_trace_throw: ruby_vminsn_type = 184; +pub const YARVINSN_trace_jump: ruby_vminsn_type = 185; +pub const YARVINSN_trace_branchif: ruby_vminsn_type = 186; +pub const YARVINSN_trace_branchunless: ruby_vminsn_type = 187; +pub const YARVINSN_trace_branchnil: ruby_vminsn_type = 188; +pub const YARVINSN_trace_jump_without_ints: ruby_vminsn_type = 189; +pub const YARVINSN_trace_branchif_without_ints: ruby_vminsn_type = 190; +pub const YARVINSN_trace_branchunless_without_ints: ruby_vminsn_type = 191; +pub const YARVINSN_trace_branchnil_without_ints: ruby_vminsn_type = 192; +pub const YARVINSN_trace_once: ruby_vminsn_type = 193; +pub const YARVINSN_trace_opt_case_dispatch: ruby_vminsn_type = 194; +pub const YARVINSN_trace_opt_plus: ruby_vminsn_type = 195; +pub const YARVINSN_trace_opt_minus: ruby_vminsn_type = 196; +pub const YARVINSN_trace_opt_mult: ruby_vminsn_type = 197; +pub const YARVINSN_trace_opt_div: ruby_vminsn_type = 198; +pub const YARVINSN_trace_opt_mod: ruby_vminsn_type = 199; +pub const YARVINSN_trace_opt_eq: ruby_vminsn_type = 200; +pub const YARVINSN_trace_opt_neq: ruby_vminsn_type = 201; +pub const YARVINSN_trace_opt_lt: ruby_vminsn_type = 202; +pub const YARVINSN_trace_opt_le: ruby_vminsn_type = 203; +pub const YARVINSN_trace_opt_gt: ruby_vminsn_type = 204; +pub const YARVINSN_trace_opt_ge: ruby_vminsn_type = 205; +pub const YARVINSN_trace_opt_ltlt: ruby_vminsn_type = 206; +pub const YARVINSN_trace_opt_and: ruby_vminsn_type = 207; +pub const YARVINSN_trace_opt_or: ruby_vminsn_type = 208; +pub const YARVINSN_trace_opt_aref: ruby_vminsn_type = 209; +pub const YARVINSN_trace_opt_aset: ruby_vminsn_type = 210; +pub const YARVINSN_trace_opt_length: ruby_vminsn_type = 211; +pub const YARVINSN_trace_opt_size: ruby_vminsn_type = 212; +pub const YARVINSN_trace_opt_empty_p: ruby_vminsn_type = 213; +pub const YARVINSN_trace_opt_succ: ruby_vminsn_type = 214; +pub const YARVINSN_trace_opt_not: ruby_vminsn_type = 215; +pub const YARVINSN_trace_opt_regexpmatch2: ruby_vminsn_type = 216; +pub const YARVINSN_trace_invokebuiltin: ruby_vminsn_type = 217; +pub const YARVINSN_trace_opt_invokebuiltin_delegate: ruby_vminsn_type = 218; +pub const YARVINSN_trace_opt_invokebuiltin_delegate_leave: ruby_vminsn_type = 219; +pub const YARVINSN_trace_getlocal_WC_0: ruby_vminsn_type = 220; +pub const YARVINSN_trace_getlocal_WC_1: ruby_vminsn_type = 221; +pub const YARVINSN_trace_setlocal_WC_0: ruby_vminsn_type = 222; +pub const YARVINSN_trace_setlocal_WC_1: ruby_vminsn_type = 223; +pub const YARVINSN_trace_putobject_INT2FIX_0_: ruby_vminsn_type = 224; +pub const YARVINSN_trace_putobject_INT2FIX_1_: ruby_vminsn_type = 225; +pub const YARVINSN_zjit_getblockparamproxy: ruby_vminsn_type = 226; +pub const YARVINSN_zjit_getinstancevariable: ruby_vminsn_type = 227; +pub const YARVINSN_zjit_setinstancevariable: ruby_vminsn_type = 228; +pub const YARVINSN_zjit_definedivar: ruby_vminsn_type = 229; +pub const YARVINSN_zjit_send: ruby_vminsn_type = 230; +pub const YARVINSN_zjit_opt_send_without_block: ruby_vminsn_type = 231; +pub const YARVINSN_zjit_objtostring: ruby_vminsn_type = 232; +pub const YARVINSN_zjit_opt_nil_p: ruby_vminsn_type = 233; +pub const YARVINSN_zjit_invokesuper: ruby_vminsn_type = 234; +pub const YARVINSN_zjit_invokeblock: ruby_vminsn_type = 235; +pub const YARVINSN_zjit_opt_plus: ruby_vminsn_type = 236; +pub const YARVINSN_zjit_opt_minus: ruby_vminsn_type = 237; +pub const YARVINSN_zjit_opt_mult: ruby_vminsn_type = 238; +pub const YARVINSN_zjit_opt_div: ruby_vminsn_type = 239; +pub const YARVINSN_zjit_opt_mod: ruby_vminsn_type = 240; +pub const YARVINSN_zjit_opt_eq: ruby_vminsn_type = 241; +pub const YARVINSN_zjit_opt_neq: ruby_vminsn_type = 242; +pub const YARVINSN_zjit_opt_lt: ruby_vminsn_type = 243; +pub const YARVINSN_zjit_opt_le: ruby_vminsn_type = 244; +pub const YARVINSN_zjit_opt_gt: ruby_vminsn_type = 245; +pub const YARVINSN_zjit_opt_ge: ruby_vminsn_type = 246; +pub const YARVINSN_zjit_opt_ltlt: ruby_vminsn_type = 247; +pub const YARVINSN_zjit_opt_and: ruby_vminsn_type = 248; +pub const YARVINSN_zjit_opt_or: ruby_vminsn_type = 249; +pub const YARVINSN_zjit_opt_aref: ruby_vminsn_type = 250; +pub const YARVINSN_zjit_opt_aset: ruby_vminsn_type = 251; +pub const YARVINSN_zjit_opt_length: ruby_vminsn_type = 252; +pub const YARVINSN_zjit_opt_size: ruby_vminsn_type = 253; +pub const YARVINSN_zjit_opt_empty_p: ruby_vminsn_type = 254; +pub const YARVINSN_zjit_opt_succ: ruby_vminsn_type = 255; +pub const YARVINSN_zjit_opt_not: ruby_vminsn_type = 256; +pub const YARVINSN_zjit_opt_regexpmatch2: ruby_vminsn_type = 257; +pub const VM_INSTRUCTION_SIZE: ruby_vminsn_type = 258; pub type ruby_vminsn_type = u32; pub type rb_iseq_callback = ::std::option::Option< unsafe extern "C" fn(arg1: *const rb_iseq_t, arg2: *mut ::std::os::raw::c_void), diff --git a/zjit/src/cruby_bindings.inc.rs b/zjit/src/cruby_bindings.inc.rs index 0cd312abf9264c..77103b9930f74c 100644 --- a/zjit/src/cruby_bindings.inc.rs +++ b/zjit/src/cruby_bindings.inc.rs @@ -585,6 +585,7 @@ pub const BUILTIN_ATTR_LEAF: rb_builtin_attr = 1; pub const BUILTIN_ATTR_SINGLE_NOARG_LEAF: rb_builtin_attr = 2; pub const BUILTIN_ATTR_INLINE_BLOCK: rb_builtin_attr = 4; pub const BUILTIN_ATTR_C_TRACE: rb_builtin_attr = 8; +pub const BUILTIN_ATTR_WITHOUT_INTERRUPTS: rb_builtin_attr = 16; pub type rb_builtin_attr = u32; pub type rb_jit_func_t = ::std::option::Option< unsafe extern "C" fn( @@ -1564,181 +1565,189 @@ pub const YARVINSN_jump: ruby_vminsn_type = 72; pub const YARVINSN_branchif: ruby_vminsn_type = 73; pub const YARVINSN_branchunless: ruby_vminsn_type = 74; pub const YARVINSN_branchnil: ruby_vminsn_type = 75; -pub const YARVINSN_once: ruby_vminsn_type = 76; -pub const YARVINSN_opt_case_dispatch: ruby_vminsn_type = 77; -pub const YARVINSN_opt_plus: ruby_vminsn_type = 78; -pub const YARVINSN_opt_minus: ruby_vminsn_type = 79; -pub const YARVINSN_opt_mult: ruby_vminsn_type = 80; -pub const YARVINSN_opt_div: ruby_vminsn_type = 81; -pub const YARVINSN_opt_mod: ruby_vminsn_type = 82; -pub const YARVINSN_opt_eq: ruby_vminsn_type = 83; -pub const YARVINSN_opt_neq: ruby_vminsn_type = 84; -pub const YARVINSN_opt_lt: ruby_vminsn_type = 85; -pub const YARVINSN_opt_le: ruby_vminsn_type = 86; -pub const YARVINSN_opt_gt: ruby_vminsn_type = 87; -pub const YARVINSN_opt_ge: ruby_vminsn_type = 88; -pub const YARVINSN_opt_ltlt: ruby_vminsn_type = 89; -pub const YARVINSN_opt_and: ruby_vminsn_type = 90; -pub const YARVINSN_opt_or: ruby_vminsn_type = 91; -pub const YARVINSN_opt_aref: ruby_vminsn_type = 92; -pub const YARVINSN_opt_aset: ruby_vminsn_type = 93; -pub const YARVINSN_opt_length: ruby_vminsn_type = 94; -pub const YARVINSN_opt_size: ruby_vminsn_type = 95; -pub const YARVINSN_opt_empty_p: ruby_vminsn_type = 96; -pub const YARVINSN_opt_succ: ruby_vminsn_type = 97; -pub const YARVINSN_opt_not: ruby_vminsn_type = 98; -pub const YARVINSN_opt_regexpmatch2: ruby_vminsn_type = 99; -pub const YARVINSN_invokebuiltin: ruby_vminsn_type = 100; -pub const YARVINSN_opt_invokebuiltin_delegate: ruby_vminsn_type = 101; -pub const YARVINSN_opt_invokebuiltin_delegate_leave: ruby_vminsn_type = 102; -pub const YARVINSN_getlocal_WC_0: ruby_vminsn_type = 103; -pub const YARVINSN_getlocal_WC_1: ruby_vminsn_type = 104; -pub const YARVINSN_setlocal_WC_0: ruby_vminsn_type = 105; -pub const YARVINSN_setlocal_WC_1: ruby_vminsn_type = 106; -pub const YARVINSN_putobject_INT2FIX_0_: ruby_vminsn_type = 107; -pub const YARVINSN_putobject_INT2FIX_1_: ruby_vminsn_type = 108; -pub const YARVINSN_trace_nop: ruby_vminsn_type = 109; -pub const YARVINSN_trace_getlocal: ruby_vminsn_type = 110; -pub const YARVINSN_trace_setlocal: ruby_vminsn_type = 111; -pub const YARVINSN_trace_getblockparam: ruby_vminsn_type = 112; -pub const YARVINSN_trace_setblockparam: ruby_vminsn_type = 113; -pub const YARVINSN_trace_getblockparamproxy: ruby_vminsn_type = 114; -pub const YARVINSN_trace_getspecial: ruby_vminsn_type = 115; -pub const YARVINSN_trace_setspecial: ruby_vminsn_type = 116; -pub const YARVINSN_trace_getinstancevariable: ruby_vminsn_type = 117; -pub const YARVINSN_trace_setinstancevariable: ruby_vminsn_type = 118; -pub const YARVINSN_trace_getclassvariable: ruby_vminsn_type = 119; -pub const YARVINSN_trace_setclassvariable: ruby_vminsn_type = 120; -pub const YARVINSN_trace_opt_getconstant_path: ruby_vminsn_type = 121; -pub const YARVINSN_trace_getconstant: ruby_vminsn_type = 122; -pub const YARVINSN_trace_setconstant: ruby_vminsn_type = 123; -pub const YARVINSN_trace_getglobal: ruby_vminsn_type = 124; -pub const YARVINSN_trace_setglobal: ruby_vminsn_type = 125; -pub const YARVINSN_trace_putnil: ruby_vminsn_type = 126; -pub const YARVINSN_trace_putself: ruby_vminsn_type = 127; -pub const YARVINSN_trace_putobject: ruby_vminsn_type = 128; -pub const YARVINSN_trace_putspecialobject: ruby_vminsn_type = 129; -pub const YARVINSN_trace_putstring: ruby_vminsn_type = 130; -pub const YARVINSN_trace_putchilledstring: ruby_vminsn_type = 131; -pub const YARVINSN_trace_concatstrings: ruby_vminsn_type = 132; -pub const YARVINSN_trace_anytostring: ruby_vminsn_type = 133; -pub const YARVINSN_trace_toregexp: ruby_vminsn_type = 134; -pub const YARVINSN_trace_intern: ruby_vminsn_type = 135; -pub const YARVINSN_trace_newarray: ruby_vminsn_type = 136; -pub const YARVINSN_trace_pushtoarraykwsplat: ruby_vminsn_type = 137; -pub const YARVINSN_trace_duparray: ruby_vminsn_type = 138; -pub const YARVINSN_trace_duphash: ruby_vminsn_type = 139; -pub const YARVINSN_trace_expandarray: ruby_vminsn_type = 140; -pub const YARVINSN_trace_concatarray: ruby_vminsn_type = 141; -pub const YARVINSN_trace_concattoarray: ruby_vminsn_type = 142; -pub const YARVINSN_trace_pushtoarray: ruby_vminsn_type = 143; -pub const YARVINSN_trace_splatarray: ruby_vminsn_type = 144; -pub const YARVINSN_trace_splatkw: ruby_vminsn_type = 145; -pub const YARVINSN_trace_newhash: ruby_vminsn_type = 146; -pub const YARVINSN_trace_newrange: ruby_vminsn_type = 147; -pub const YARVINSN_trace_pop: ruby_vminsn_type = 148; -pub const YARVINSN_trace_dup: ruby_vminsn_type = 149; -pub const YARVINSN_trace_dupn: ruby_vminsn_type = 150; -pub const YARVINSN_trace_swap: ruby_vminsn_type = 151; -pub const YARVINSN_trace_opt_reverse: ruby_vminsn_type = 152; -pub const YARVINSN_trace_topn: ruby_vminsn_type = 153; -pub const YARVINSN_trace_setn: ruby_vminsn_type = 154; -pub const YARVINSN_trace_adjuststack: ruby_vminsn_type = 155; -pub const YARVINSN_trace_defined: ruby_vminsn_type = 156; -pub const YARVINSN_trace_definedivar: ruby_vminsn_type = 157; -pub const YARVINSN_trace_checkmatch: ruby_vminsn_type = 158; -pub const YARVINSN_trace_checkkeyword: ruby_vminsn_type = 159; -pub const YARVINSN_trace_checktype: ruby_vminsn_type = 160; -pub const YARVINSN_trace_defineclass: ruby_vminsn_type = 161; -pub const YARVINSN_trace_definemethod: ruby_vminsn_type = 162; -pub const YARVINSN_trace_definesmethod: ruby_vminsn_type = 163; -pub const YARVINSN_trace_send: ruby_vminsn_type = 164; -pub const YARVINSN_trace_sendforward: ruby_vminsn_type = 165; -pub const YARVINSN_trace_opt_send_without_block: ruby_vminsn_type = 166; -pub const YARVINSN_trace_opt_new: ruby_vminsn_type = 167; -pub const YARVINSN_trace_objtostring: ruby_vminsn_type = 168; -pub const YARVINSN_trace_opt_ary_freeze: ruby_vminsn_type = 169; -pub const YARVINSN_trace_opt_hash_freeze: ruby_vminsn_type = 170; -pub const YARVINSN_trace_opt_str_freeze: ruby_vminsn_type = 171; -pub const YARVINSN_trace_opt_nil_p: ruby_vminsn_type = 172; -pub const YARVINSN_trace_opt_str_uminus: ruby_vminsn_type = 173; -pub const YARVINSN_trace_opt_duparray_send: ruby_vminsn_type = 174; -pub const YARVINSN_trace_opt_newarray_send: ruby_vminsn_type = 175; -pub const YARVINSN_trace_invokesuper: ruby_vminsn_type = 176; -pub const YARVINSN_trace_invokesuperforward: ruby_vminsn_type = 177; -pub const YARVINSN_trace_invokeblock: ruby_vminsn_type = 178; -pub const YARVINSN_trace_leave: ruby_vminsn_type = 179; -pub const YARVINSN_trace_throw: ruby_vminsn_type = 180; -pub const YARVINSN_trace_jump: ruby_vminsn_type = 181; -pub const YARVINSN_trace_branchif: ruby_vminsn_type = 182; -pub const YARVINSN_trace_branchunless: ruby_vminsn_type = 183; -pub const YARVINSN_trace_branchnil: ruby_vminsn_type = 184; -pub const YARVINSN_trace_once: ruby_vminsn_type = 185; -pub const YARVINSN_trace_opt_case_dispatch: ruby_vminsn_type = 186; -pub const YARVINSN_trace_opt_plus: ruby_vminsn_type = 187; -pub const YARVINSN_trace_opt_minus: ruby_vminsn_type = 188; -pub const YARVINSN_trace_opt_mult: ruby_vminsn_type = 189; -pub const YARVINSN_trace_opt_div: ruby_vminsn_type = 190; -pub const YARVINSN_trace_opt_mod: ruby_vminsn_type = 191; -pub const YARVINSN_trace_opt_eq: ruby_vminsn_type = 192; -pub const YARVINSN_trace_opt_neq: ruby_vminsn_type = 193; -pub const YARVINSN_trace_opt_lt: ruby_vminsn_type = 194; -pub const YARVINSN_trace_opt_le: ruby_vminsn_type = 195; -pub const YARVINSN_trace_opt_gt: ruby_vminsn_type = 196; -pub const YARVINSN_trace_opt_ge: ruby_vminsn_type = 197; -pub const YARVINSN_trace_opt_ltlt: ruby_vminsn_type = 198; -pub const YARVINSN_trace_opt_and: ruby_vminsn_type = 199; -pub const YARVINSN_trace_opt_or: ruby_vminsn_type = 200; -pub const YARVINSN_trace_opt_aref: ruby_vminsn_type = 201; -pub const YARVINSN_trace_opt_aset: ruby_vminsn_type = 202; -pub const YARVINSN_trace_opt_length: ruby_vminsn_type = 203; -pub const YARVINSN_trace_opt_size: ruby_vminsn_type = 204; -pub const YARVINSN_trace_opt_empty_p: ruby_vminsn_type = 205; -pub const YARVINSN_trace_opt_succ: ruby_vminsn_type = 206; -pub const YARVINSN_trace_opt_not: ruby_vminsn_type = 207; -pub const YARVINSN_trace_opt_regexpmatch2: ruby_vminsn_type = 208; -pub const YARVINSN_trace_invokebuiltin: ruby_vminsn_type = 209; -pub const YARVINSN_trace_opt_invokebuiltin_delegate: ruby_vminsn_type = 210; -pub const YARVINSN_trace_opt_invokebuiltin_delegate_leave: ruby_vminsn_type = 211; -pub const YARVINSN_trace_getlocal_WC_0: ruby_vminsn_type = 212; -pub const YARVINSN_trace_getlocal_WC_1: ruby_vminsn_type = 213; -pub const YARVINSN_trace_setlocal_WC_0: ruby_vminsn_type = 214; -pub const YARVINSN_trace_setlocal_WC_1: ruby_vminsn_type = 215; -pub const YARVINSN_trace_putobject_INT2FIX_0_: ruby_vminsn_type = 216; -pub const YARVINSN_trace_putobject_INT2FIX_1_: ruby_vminsn_type = 217; -pub const YARVINSN_zjit_getblockparamproxy: ruby_vminsn_type = 218; -pub const YARVINSN_zjit_getinstancevariable: ruby_vminsn_type = 219; -pub const YARVINSN_zjit_setinstancevariable: ruby_vminsn_type = 220; -pub const YARVINSN_zjit_definedivar: ruby_vminsn_type = 221; -pub const YARVINSN_zjit_send: ruby_vminsn_type = 222; -pub const YARVINSN_zjit_opt_send_without_block: ruby_vminsn_type = 223; -pub const YARVINSN_zjit_objtostring: ruby_vminsn_type = 224; -pub const YARVINSN_zjit_opt_nil_p: ruby_vminsn_type = 225; -pub const YARVINSN_zjit_invokesuper: ruby_vminsn_type = 226; -pub const YARVINSN_zjit_invokeblock: ruby_vminsn_type = 227; -pub const YARVINSN_zjit_opt_plus: ruby_vminsn_type = 228; -pub const YARVINSN_zjit_opt_minus: ruby_vminsn_type = 229; -pub const YARVINSN_zjit_opt_mult: ruby_vminsn_type = 230; -pub const YARVINSN_zjit_opt_div: ruby_vminsn_type = 231; -pub const YARVINSN_zjit_opt_mod: ruby_vminsn_type = 232; -pub const YARVINSN_zjit_opt_eq: ruby_vminsn_type = 233; -pub const YARVINSN_zjit_opt_neq: ruby_vminsn_type = 234; -pub const YARVINSN_zjit_opt_lt: ruby_vminsn_type = 235; -pub const YARVINSN_zjit_opt_le: ruby_vminsn_type = 236; -pub const YARVINSN_zjit_opt_gt: ruby_vminsn_type = 237; -pub const YARVINSN_zjit_opt_ge: ruby_vminsn_type = 238; -pub const YARVINSN_zjit_opt_ltlt: ruby_vminsn_type = 239; -pub const YARVINSN_zjit_opt_and: ruby_vminsn_type = 240; -pub const YARVINSN_zjit_opt_or: ruby_vminsn_type = 241; -pub const YARVINSN_zjit_opt_aref: ruby_vminsn_type = 242; -pub const YARVINSN_zjit_opt_aset: ruby_vminsn_type = 243; -pub const YARVINSN_zjit_opt_length: ruby_vminsn_type = 244; -pub const YARVINSN_zjit_opt_size: ruby_vminsn_type = 245; -pub const YARVINSN_zjit_opt_empty_p: ruby_vminsn_type = 246; -pub const YARVINSN_zjit_opt_succ: ruby_vminsn_type = 247; -pub const YARVINSN_zjit_opt_not: ruby_vminsn_type = 248; -pub const YARVINSN_zjit_opt_regexpmatch2: ruby_vminsn_type = 249; -pub const VM_INSTRUCTION_SIZE: ruby_vminsn_type = 250; +pub const YARVINSN_jump_without_ints: ruby_vminsn_type = 76; +pub const YARVINSN_branchif_without_ints: ruby_vminsn_type = 77; +pub const YARVINSN_branchunless_without_ints: ruby_vminsn_type = 78; +pub const YARVINSN_branchnil_without_ints: ruby_vminsn_type = 79; +pub const YARVINSN_once: ruby_vminsn_type = 80; +pub const YARVINSN_opt_case_dispatch: ruby_vminsn_type = 81; +pub const YARVINSN_opt_plus: ruby_vminsn_type = 82; +pub const YARVINSN_opt_minus: ruby_vminsn_type = 83; +pub const YARVINSN_opt_mult: ruby_vminsn_type = 84; +pub const YARVINSN_opt_div: ruby_vminsn_type = 85; +pub const YARVINSN_opt_mod: ruby_vminsn_type = 86; +pub const YARVINSN_opt_eq: ruby_vminsn_type = 87; +pub const YARVINSN_opt_neq: ruby_vminsn_type = 88; +pub const YARVINSN_opt_lt: ruby_vminsn_type = 89; +pub const YARVINSN_opt_le: ruby_vminsn_type = 90; +pub const YARVINSN_opt_gt: ruby_vminsn_type = 91; +pub const YARVINSN_opt_ge: ruby_vminsn_type = 92; +pub const YARVINSN_opt_ltlt: ruby_vminsn_type = 93; +pub const YARVINSN_opt_and: ruby_vminsn_type = 94; +pub const YARVINSN_opt_or: ruby_vminsn_type = 95; +pub const YARVINSN_opt_aref: ruby_vminsn_type = 96; +pub const YARVINSN_opt_aset: ruby_vminsn_type = 97; +pub const YARVINSN_opt_length: ruby_vminsn_type = 98; +pub const YARVINSN_opt_size: ruby_vminsn_type = 99; +pub const YARVINSN_opt_empty_p: ruby_vminsn_type = 100; +pub const YARVINSN_opt_succ: ruby_vminsn_type = 101; +pub const YARVINSN_opt_not: ruby_vminsn_type = 102; +pub const YARVINSN_opt_regexpmatch2: ruby_vminsn_type = 103; +pub const YARVINSN_invokebuiltin: ruby_vminsn_type = 104; +pub const YARVINSN_opt_invokebuiltin_delegate: ruby_vminsn_type = 105; +pub const YARVINSN_opt_invokebuiltin_delegate_leave: ruby_vminsn_type = 106; +pub const YARVINSN_getlocal_WC_0: ruby_vminsn_type = 107; +pub const YARVINSN_getlocal_WC_1: ruby_vminsn_type = 108; +pub const YARVINSN_setlocal_WC_0: ruby_vminsn_type = 109; +pub const YARVINSN_setlocal_WC_1: ruby_vminsn_type = 110; +pub const YARVINSN_putobject_INT2FIX_0_: ruby_vminsn_type = 111; +pub const YARVINSN_putobject_INT2FIX_1_: ruby_vminsn_type = 112; +pub const YARVINSN_trace_nop: ruby_vminsn_type = 113; +pub const YARVINSN_trace_getlocal: ruby_vminsn_type = 114; +pub const YARVINSN_trace_setlocal: ruby_vminsn_type = 115; +pub const YARVINSN_trace_getblockparam: ruby_vminsn_type = 116; +pub const YARVINSN_trace_setblockparam: ruby_vminsn_type = 117; +pub const YARVINSN_trace_getblockparamproxy: ruby_vminsn_type = 118; +pub const YARVINSN_trace_getspecial: ruby_vminsn_type = 119; +pub const YARVINSN_trace_setspecial: ruby_vminsn_type = 120; +pub const YARVINSN_trace_getinstancevariable: ruby_vminsn_type = 121; +pub const YARVINSN_trace_setinstancevariable: ruby_vminsn_type = 122; +pub const YARVINSN_trace_getclassvariable: ruby_vminsn_type = 123; +pub const YARVINSN_trace_setclassvariable: ruby_vminsn_type = 124; +pub const YARVINSN_trace_opt_getconstant_path: ruby_vminsn_type = 125; +pub const YARVINSN_trace_getconstant: ruby_vminsn_type = 126; +pub const YARVINSN_trace_setconstant: ruby_vminsn_type = 127; +pub const YARVINSN_trace_getglobal: ruby_vminsn_type = 128; +pub const YARVINSN_trace_setglobal: ruby_vminsn_type = 129; +pub const YARVINSN_trace_putnil: ruby_vminsn_type = 130; +pub const YARVINSN_trace_putself: ruby_vminsn_type = 131; +pub const YARVINSN_trace_putobject: ruby_vminsn_type = 132; +pub const YARVINSN_trace_putspecialobject: ruby_vminsn_type = 133; +pub const YARVINSN_trace_putstring: ruby_vminsn_type = 134; +pub const YARVINSN_trace_putchilledstring: ruby_vminsn_type = 135; +pub const YARVINSN_trace_concatstrings: ruby_vminsn_type = 136; +pub const YARVINSN_trace_anytostring: ruby_vminsn_type = 137; +pub const YARVINSN_trace_toregexp: ruby_vminsn_type = 138; +pub const YARVINSN_trace_intern: ruby_vminsn_type = 139; +pub const YARVINSN_trace_newarray: ruby_vminsn_type = 140; +pub const YARVINSN_trace_pushtoarraykwsplat: ruby_vminsn_type = 141; +pub const YARVINSN_trace_duparray: ruby_vminsn_type = 142; +pub const YARVINSN_trace_duphash: ruby_vminsn_type = 143; +pub const YARVINSN_trace_expandarray: ruby_vminsn_type = 144; +pub const YARVINSN_trace_concatarray: ruby_vminsn_type = 145; +pub const YARVINSN_trace_concattoarray: ruby_vminsn_type = 146; +pub const YARVINSN_trace_pushtoarray: ruby_vminsn_type = 147; +pub const YARVINSN_trace_splatarray: ruby_vminsn_type = 148; +pub const YARVINSN_trace_splatkw: ruby_vminsn_type = 149; +pub const YARVINSN_trace_newhash: ruby_vminsn_type = 150; +pub const YARVINSN_trace_newrange: ruby_vminsn_type = 151; +pub const YARVINSN_trace_pop: ruby_vminsn_type = 152; +pub const YARVINSN_trace_dup: ruby_vminsn_type = 153; +pub const YARVINSN_trace_dupn: ruby_vminsn_type = 154; +pub const YARVINSN_trace_swap: ruby_vminsn_type = 155; +pub const YARVINSN_trace_opt_reverse: ruby_vminsn_type = 156; +pub const YARVINSN_trace_topn: ruby_vminsn_type = 157; +pub const YARVINSN_trace_setn: ruby_vminsn_type = 158; +pub const YARVINSN_trace_adjuststack: ruby_vminsn_type = 159; +pub const YARVINSN_trace_defined: ruby_vminsn_type = 160; +pub const YARVINSN_trace_definedivar: ruby_vminsn_type = 161; +pub const YARVINSN_trace_checkmatch: ruby_vminsn_type = 162; +pub const YARVINSN_trace_checkkeyword: ruby_vminsn_type = 163; +pub const YARVINSN_trace_checktype: ruby_vminsn_type = 164; +pub const YARVINSN_trace_defineclass: ruby_vminsn_type = 165; +pub const YARVINSN_trace_definemethod: ruby_vminsn_type = 166; +pub const YARVINSN_trace_definesmethod: ruby_vminsn_type = 167; +pub const YARVINSN_trace_send: ruby_vminsn_type = 168; +pub const YARVINSN_trace_sendforward: ruby_vminsn_type = 169; +pub const YARVINSN_trace_opt_send_without_block: ruby_vminsn_type = 170; +pub const YARVINSN_trace_opt_new: ruby_vminsn_type = 171; +pub const YARVINSN_trace_objtostring: ruby_vminsn_type = 172; +pub const YARVINSN_trace_opt_ary_freeze: ruby_vminsn_type = 173; +pub const YARVINSN_trace_opt_hash_freeze: ruby_vminsn_type = 174; +pub const YARVINSN_trace_opt_str_freeze: ruby_vminsn_type = 175; +pub const YARVINSN_trace_opt_nil_p: ruby_vminsn_type = 176; +pub const YARVINSN_trace_opt_str_uminus: ruby_vminsn_type = 177; +pub const YARVINSN_trace_opt_duparray_send: ruby_vminsn_type = 178; +pub const YARVINSN_trace_opt_newarray_send: ruby_vminsn_type = 179; +pub const YARVINSN_trace_invokesuper: ruby_vminsn_type = 180; +pub const YARVINSN_trace_invokesuperforward: ruby_vminsn_type = 181; +pub const YARVINSN_trace_invokeblock: ruby_vminsn_type = 182; +pub const YARVINSN_trace_leave: ruby_vminsn_type = 183; +pub const YARVINSN_trace_throw: ruby_vminsn_type = 184; +pub const YARVINSN_trace_jump: ruby_vminsn_type = 185; +pub const YARVINSN_trace_branchif: ruby_vminsn_type = 186; +pub const YARVINSN_trace_branchunless: ruby_vminsn_type = 187; +pub const YARVINSN_trace_branchnil: ruby_vminsn_type = 188; +pub const YARVINSN_trace_jump_without_ints: ruby_vminsn_type = 189; +pub const YARVINSN_trace_branchif_without_ints: ruby_vminsn_type = 190; +pub const YARVINSN_trace_branchunless_without_ints: ruby_vminsn_type = 191; +pub const YARVINSN_trace_branchnil_without_ints: ruby_vminsn_type = 192; +pub const YARVINSN_trace_once: ruby_vminsn_type = 193; +pub const YARVINSN_trace_opt_case_dispatch: ruby_vminsn_type = 194; +pub const YARVINSN_trace_opt_plus: ruby_vminsn_type = 195; +pub const YARVINSN_trace_opt_minus: ruby_vminsn_type = 196; +pub const YARVINSN_trace_opt_mult: ruby_vminsn_type = 197; +pub const YARVINSN_trace_opt_div: ruby_vminsn_type = 198; +pub const YARVINSN_trace_opt_mod: ruby_vminsn_type = 199; +pub const YARVINSN_trace_opt_eq: ruby_vminsn_type = 200; +pub const YARVINSN_trace_opt_neq: ruby_vminsn_type = 201; +pub const YARVINSN_trace_opt_lt: ruby_vminsn_type = 202; +pub const YARVINSN_trace_opt_le: ruby_vminsn_type = 203; +pub const YARVINSN_trace_opt_gt: ruby_vminsn_type = 204; +pub const YARVINSN_trace_opt_ge: ruby_vminsn_type = 205; +pub const YARVINSN_trace_opt_ltlt: ruby_vminsn_type = 206; +pub const YARVINSN_trace_opt_and: ruby_vminsn_type = 207; +pub const YARVINSN_trace_opt_or: ruby_vminsn_type = 208; +pub const YARVINSN_trace_opt_aref: ruby_vminsn_type = 209; +pub const YARVINSN_trace_opt_aset: ruby_vminsn_type = 210; +pub const YARVINSN_trace_opt_length: ruby_vminsn_type = 211; +pub const YARVINSN_trace_opt_size: ruby_vminsn_type = 212; +pub const YARVINSN_trace_opt_empty_p: ruby_vminsn_type = 213; +pub const YARVINSN_trace_opt_succ: ruby_vminsn_type = 214; +pub const YARVINSN_trace_opt_not: ruby_vminsn_type = 215; +pub const YARVINSN_trace_opt_regexpmatch2: ruby_vminsn_type = 216; +pub const YARVINSN_trace_invokebuiltin: ruby_vminsn_type = 217; +pub const YARVINSN_trace_opt_invokebuiltin_delegate: ruby_vminsn_type = 218; +pub const YARVINSN_trace_opt_invokebuiltin_delegate_leave: ruby_vminsn_type = 219; +pub const YARVINSN_trace_getlocal_WC_0: ruby_vminsn_type = 220; +pub const YARVINSN_trace_getlocal_WC_1: ruby_vminsn_type = 221; +pub const YARVINSN_trace_setlocal_WC_0: ruby_vminsn_type = 222; +pub const YARVINSN_trace_setlocal_WC_1: ruby_vminsn_type = 223; +pub const YARVINSN_trace_putobject_INT2FIX_0_: ruby_vminsn_type = 224; +pub const YARVINSN_trace_putobject_INT2FIX_1_: ruby_vminsn_type = 225; +pub const YARVINSN_zjit_getblockparamproxy: ruby_vminsn_type = 226; +pub const YARVINSN_zjit_getinstancevariable: ruby_vminsn_type = 227; +pub const YARVINSN_zjit_setinstancevariable: ruby_vminsn_type = 228; +pub const YARVINSN_zjit_definedivar: ruby_vminsn_type = 229; +pub const YARVINSN_zjit_send: ruby_vminsn_type = 230; +pub const YARVINSN_zjit_opt_send_without_block: ruby_vminsn_type = 231; +pub const YARVINSN_zjit_objtostring: ruby_vminsn_type = 232; +pub const YARVINSN_zjit_opt_nil_p: ruby_vminsn_type = 233; +pub const YARVINSN_zjit_invokesuper: ruby_vminsn_type = 234; +pub const YARVINSN_zjit_invokeblock: ruby_vminsn_type = 235; +pub const YARVINSN_zjit_opt_plus: ruby_vminsn_type = 236; +pub const YARVINSN_zjit_opt_minus: ruby_vminsn_type = 237; +pub const YARVINSN_zjit_opt_mult: ruby_vminsn_type = 238; +pub const YARVINSN_zjit_opt_div: ruby_vminsn_type = 239; +pub const YARVINSN_zjit_opt_mod: ruby_vminsn_type = 240; +pub const YARVINSN_zjit_opt_eq: ruby_vminsn_type = 241; +pub const YARVINSN_zjit_opt_neq: ruby_vminsn_type = 242; +pub const YARVINSN_zjit_opt_lt: ruby_vminsn_type = 243; +pub const YARVINSN_zjit_opt_le: ruby_vminsn_type = 244; +pub const YARVINSN_zjit_opt_gt: ruby_vminsn_type = 245; +pub const YARVINSN_zjit_opt_ge: ruby_vminsn_type = 246; +pub const YARVINSN_zjit_opt_ltlt: ruby_vminsn_type = 247; +pub const YARVINSN_zjit_opt_and: ruby_vminsn_type = 248; +pub const YARVINSN_zjit_opt_or: ruby_vminsn_type = 249; +pub const YARVINSN_zjit_opt_aref: ruby_vminsn_type = 250; +pub const YARVINSN_zjit_opt_aset: ruby_vminsn_type = 251; +pub const YARVINSN_zjit_opt_length: ruby_vminsn_type = 252; +pub const YARVINSN_zjit_opt_size: ruby_vminsn_type = 253; +pub const YARVINSN_zjit_opt_empty_p: ruby_vminsn_type = 254; +pub const YARVINSN_zjit_opt_succ: ruby_vminsn_type = 255; +pub const YARVINSN_zjit_opt_not: ruby_vminsn_type = 256; +pub const YARVINSN_zjit_opt_regexpmatch2: ruby_vminsn_type = 257; +pub const VM_INSTRUCTION_SIZE: ruby_vminsn_type = 258; pub type ruby_vminsn_type = u32; pub type rb_iseq_callback = ::std::option::Option< unsafe extern "C" fn(arg1: *const rb_iseq_t, arg2: *mut ::std::os::raw::c_void), diff --git a/zjit/src/hir.rs b/zjit/src/hir.rs index fe0122d2973471..cf2371ef5a416b 100644 --- a/zjit/src/hir.rs +++ b/zjit/src/hir.rs @@ -6119,7 +6119,8 @@ fn compute_bytecode_info(iseq: *const rb_iseq_t, opt_table: &[u32]) -> BytecodeI .unwrap(); insn_idx += insn_len(opcode as usize); match opcode { - YARVINSN_branchunless | YARVINSN_jump | YARVINSN_branchif | YARVINSN_branchnil => { + YARVINSN_branchunless | YARVINSN_jump | YARVINSN_branchif | YARVINSN_branchnil + | YARVINSN_branchunless_without_ints | YARVINSN_jump_without_ints | YARVINSN_branchif_without_ints | YARVINSN_branchnil_without_ints => { let offset = get_arg(pc, 0).as_i64(); jump_targets.insert(insn_idx_at_offset(insn_idx, offset)); } @@ -6232,6 +6233,10 @@ fn invalidates_locals(opcode: u32, operands: *const VALUE) -> bool { | YARVINSN_branchunless | YARVINSN_branchif | YARVINSN_branchnil + | YARVINSN_jump_without_ints + | YARVINSN_branchunless_without_ints + | YARVINSN_branchif_without_ints + | YARVINSN_branchnil_without_ints | YARVINSN_leave => false, // TODO(max): Read the invokebuiltin target from operands and determine if it's leaf _ => unsafe { !rb_zjit_insn_leaf(opcode as i32, operands) } @@ -6655,8 +6660,10 @@ pub fn iseq_to_hir(iseq: *const rb_iseq_t) -> Result { let snapshot = fun.push_insn(block, Insn::Snapshot { state: exit_state }); state.stack_push(fun.push_insn(block, Insn::GetConstantPath { ic, state: snapshot })); } - YARVINSN_branchunless => { - fun.push_insn(block, Insn::CheckInterrupts { state: exit_id }); + YARVINSN_branchunless | YARVINSN_branchunless_without_ints => { + if opcode == YARVINSN_branchunless { + fun.push_insn(block, Insn::CheckInterrupts { state: exit_id }); + } let offset = get_arg(pc, 0).as_i64(); let val = state.stack_pop()?; let test_id = fun.push_insn(block, Insn::Test { val }); @@ -6675,8 +6682,10 @@ pub fn iseq_to_hir(iseq: *const rb_iseq_t) -> Result { state.replace(val, not_nil_false); queue.push_back((state.clone(), target, target_idx, local_inval)); } - YARVINSN_branchif => { - fun.push_insn(block, Insn::CheckInterrupts { state: exit_id }); + YARVINSN_branchif | YARVINSN_branchif_without_ints => { + if opcode == YARVINSN_branchif { + fun.push_insn(block, Insn::CheckInterrupts { state: exit_id }); + } let offset = get_arg(pc, 0).as_i64(); let val = state.stack_pop()?; let test_id = fun.push_insn(block, Insn::Test { val }); @@ -6695,8 +6704,10 @@ pub fn iseq_to_hir(iseq: *const rb_iseq_t) -> Result { state.replace(val, nil_false); queue.push_back((state.clone(), target, target_idx, local_inval)); } - YARVINSN_branchnil => { - fun.push_insn(block, Insn::CheckInterrupts { state: exit_id }); + YARVINSN_branchnil | YARVINSN_branchnil_without_ints => { + if opcode == YARVINSN_branchnil { + fun.push_insn(block, Insn::CheckInterrupts { state: exit_id }); + } let offset = get_arg(pc, 0).as_i64(); let val = state.stack_pop()?; let test_id = fun.push_insn(block, Insn::IsNil { val }); @@ -6745,9 +6756,11 @@ pub fn iseq_to_hir(iseq: *const rb_iseq_t) -> Result { state.stack_setn(argc, insn_id); state.stack_setn(argc + 1, insn_id); } - YARVINSN_jump => { + YARVINSN_jump | YARVINSN_jump_without_ints => { let offset = get_arg(pc, 0).as_i64(); - fun.push_insn(block, Insn::CheckInterrupts { state: exit_id }); + if opcode == YARVINSN_jump { + fun.push_insn(block, Insn::CheckInterrupts { state: exit_id }); + } let target_idx = insn_idx_at_offset(insn_idx, offset); let target = insn_idx_to_block[&target_idx]; let _branch_id = fun.push_insn(block, Insn::Jump( From f96f84837d5dfedaee9758141fd2006ffb03e96d Mon Sep 17 00:00:00 2001 From: Max Bernstein Date: Fri, 6 Feb 2026 17:40:17 -0500 Subject: [PATCH 6/9] Replace ary_fetch_next with separate array iteration primitives --- array.c | 27 +++++++++++++++++---------- array.rb | 32 +++++++++++++++++--------------- 2 files changed, 34 insertions(+), 25 deletions(-) diff --git a/array.c b/array.c index 90e83fd3ad972f..876642dff71f94 100644 --- a/array.c +++ b/array.c @@ -2694,18 +2694,25 @@ ary_enum_length(VALUE ary, VALUE args, VALUE eobj) return rb_ary_length(ary); } -// Primitive to avoid a race condition in Array#each. -// Return `true` and write `value` and `index` if the element exists. +// Return true if the index is at or past the end of the array. static VALUE -ary_fetch_next(VALUE self, VALUE *index, VALUE *value) +rb_jit_ary_at_end(rb_execution_context_t *ec, VALUE self, VALUE index) { - long i = NUM2LONG(*index); - if (i >= RARRAY_LEN(self)) { - return Qfalse; - } - *value = RARRAY_AREF(self, i); - *index = LONG2NUM(i + 1); - return Qtrue; + return FIX2LONG(index) >= RARRAY_LEN(self) ? Qtrue : Qfalse; +} + +// Return the element at the given fixnum index. +static VALUE +rb_jit_ary_at(rb_execution_context_t *ec, VALUE self, VALUE index) +{ + return RARRAY_AREF(self, FIX2LONG(index)); +} + +// Increment a fixnum by 1. +static VALUE +rb_jit_fixnum_inc(rb_execution_context_t *ec, VALUE self, VALUE num) +{ + return LONG2FIX(FIX2LONG(num) + 1); } /* diff --git a/array.rb b/array.rb index 4fee5ecaebe7c9..e21de1a5b2496e 100644 --- a/array.rb +++ b/array.rb @@ -222,10 +222,10 @@ def each # :nodoc: unless defined?(yield) return Primitive.cexpr! 'SIZED_ENUMERATOR(self, 0, 0, ary_enum_length)' end - _i = 0 - value = nil - while Primitive.cexpr!(%q{ ary_fetch_next(self, LOCAL_PTR(_i), LOCAL_PTR(value)) }) - yield value + i = 0 + until Primitive.rb_jit_ary_at_end(i) + yield Primitive.rb_jit_ary_at(i) + i = Primitive.rb_jit_fixnum_inc(i) end self end @@ -241,12 +241,12 @@ def map # :nodoc: return Primitive.cexpr! 'SIZED_ENUMERATOR(self, 0, 0, ary_enum_length)' end - _i = 0 - value = nil + i = 0 result = Primitive.ary_sized_alloc - while Primitive.cexpr!(%q{ ary_fetch_next(self, LOCAL_PTR(_i), LOCAL_PTR(value)) }) - value = yield(value) - Primitive.cexpr!(%q{ rb_ary_push(result, value) }) + until Primitive.rb_jit_ary_at_end(i) + _value = yield(Primitive.rb_jit_ary_at(i)) + Primitive.cexpr!(%q{ rb_ary_push(result, _value) }) + i = Primitive.rb_jit_fixnum_inc(i) end result end @@ -267,13 +267,14 @@ def select # :nodoc: return Primitive.cexpr! 'SIZED_ENUMERATOR(self, 0, 0, ary_enum_length)' end - _i = 0 - value = nil + i = 0 result = Primitive.ary_sized_alloc - while Primitive.cexpr!(%q{ ary_fetch_next(self, LOCAL_PTR(_i), LOCAL_PTR(value)) }) + until Primitive.rb_jit_ary_at_end(i) + value = Primitive.rb_jit_ary_at(i) if yield value Primitive.cexpr!(%q{ rb_ary_push(result, value) }) end + i = Primitive.rb_jit_fixnum_inc(i) end result end @@ -293,10 +294,11 @@ def find(if_none_proc = nil) # :nodoc: unless defined?(yield) return Primitive.cexpr! 'SIZED_ENUMERATOR(self, 0, 0, ary_enum_length)' end - _i = 0 - value = nil - while Primitive.cexpr!(%q{ ary_fetch_next(self, LOCAL_PTR(_i), LOCAL_PTR(value)) }) + i = 0 + until Primitive.rb_jit_ary_at_end(i) + value = Primitive.rb_jit_ary_at(i) return value if yield(value) + i = Primitive.rb_jit_fixnum_inc(i) end if_none_proc&.call end From 98e6f5e4bc56b8b611d152a43500531478a6472d Mon Sep 17 00:00:00 2001 From: Max Bernstein Date: Fri, 6 Feb 2026 17:45:58 -0500 Subject: [PATCH 7/9] Enable ZJIT jit hooks for with_jit builtins --- jit_hook.rb | 6 ++-- zjit/bindgen/src/main.rs | 2 ++ zjit/src/cruby.rs | 6 ++++ zjit/src/cruby_bindings.inc.rs | 2 ++ zjit/src/hir/opt_tests.rs | 50 ++++++++++++++++++++++++++++++---- zjit/src/hir/tests.rs | 48 ++++++++++++++++++++++++++++++++ zjit/src/state.rs | 8 ++++-- 7 files changed, 112 insertions(+), 10 deletions(-) diff --git a/jit_hook.rb b/jit_hook.rb index 346b7169480031..c605d6e26d3e7a 100644 --- a/jit_hook.rb +++ b/jit_hook.rb @@ -2,10 +2,10 @@ class Module # Internal helper for built-in initializations to define methods only when JIT is enabled. # This method is removed in jit_undef.rb. private def with_jit(&block) # :nodoc: - # ZJIT currently doesn't compile Array#each properly, so it's disabled for now. - if defined?(RubyVM::ZJIT) && false # TODO: remove `&& false` (Shopify/ruby#667) + if defined?(RubyVM::ZJIT) RubyVM::ZJIT.send(:add_jit_hook, block) - elsif defined?(RubyVM::YJIT) + end + if defined?(RubyVM::YJIT) RubyVM::YJIT.send(:add_jit_hook, block) end end diff --git a/zjit/bindgen/src/main.rs b/zjit/bindgen/src/main.rs index d71e75c444af69..dab1a6d92985d6 100644 --- a/zjit/bindgen/src/main.rs +++ b/zjit/bindgen/src/main.rs @@ -167,6 +167,8 @@ fn main() { .allowlist_var("rb_cClass") .allowlist_var("rb_cRegexp") .allowlist_var("rb_cISeq") + .allowlist_var("rb_cRubyVM") + .allowlist_function("rb_const_get") .allowlist_type("ruby_fl_type") .allowlist_type("ruby_fl_ushift") diff --git a/zjit/src/cruby.rs b/zjit/src/cruby.rs index 8e569793a87eb5..a47d9bf61f60a2 100644 --- a/zjit/src/cruby.rs +++ b/zjit/src/cruby.rs @@ -1133,6 +1133,12 @@ pub mod test_utils { crate::cruby::ids::init(); // for ID! usages in tests } + // Call ZJIT hooks to install Ruby implementations of builtins like Array#each + unsafe { + let zjit = rb_const_get(rb_cRubyVM, rust_str_to_id("ZJIT")); + rb_funcallv(zjit, rust_str_to_id("call_jit_hooks"), 0, std::ptr::null()); + } + // Set up globals for convenience let zjit_entry = ZJITState::init(); diff --git a/zjit/src/cruby_bindings.inc.rs b/zjit/src/cruby_bindings.inc.rs index 77103b9930f74c..f178e7672894d9 100644 --- a/zjit/src/cruby_bindings.inc.rs +++ b/zjit/src/cruby_bindings.inc.rs @@ -1960,6 +1960,7 @@ unsafe extern "C" { pub fn rb_ivar_set(obj: VALUE, name: ID, val: VALUE) -> VALUE; pub fn rb_ivar_defined(obj: VALUE, name: ID) -> VALUE; pub fn rb_attr_get(obj: VALUE, name: ID) -> VALUE; + pub fn rb_const_get(space: VALUE, name: ID) -> VALUE; 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; @@ -1982,6 +1983,7 @@ unsafe extern "C" { id: ID, ) -> *const rb_callable_method_entry_t; pub static mut rb_cISeq: VALUE; + pub static mut rb_cRubyVM: VALUE; pub static mut rb_mRubyVMFrozenCore: VALUE; pub static mut rb_block_param_proxy: VALUE; pub fn rb_vm_ep_local_ep(ep: *const VALUE) -> *const VALUE; diff --git a/zjit/src/hir/opt_tests.rs b/zjit/src/hir/opt_tests.rs index 4ce01c438af578..8a172e6b128f10 100644 --- a/zjit/src/hir/opt_tests.rs +++ b/zjit/src/hir/opt_tests.rs @@ -823,11 +823,11 @@ mod hir_opt_tests { bb2(v8:BasicObject, v9:BasicObject): PatchPoint NoSingletonClass(C@0x1000) PatchPoint MethodRedefined(C@0x1000, fun_new_map@0x1008, cme:0x1010) - v23:ArraySubclass[class_exact:C] = GuardType v9, ArraySubclass[class_exact:C] - v24:BasicObject = CCallWithFrame v23, :C#fun_new_map@0x1038, block=0x1040 + v22:ArraySubclass[class_exact:C] = GuardType v9, ArraySubclass[class_exact:C] + v23:BasicObject = SendDirect v22, 0x1038, :fun_new_map (0x1048) v15:BasicObject = GetLocal :o, l0, EP@3 CheckInterrupts - Return v24 + Return v23 "); } @@ -6515,9 +6515,9 @@ mod hir_opt_tests { v11:ArrayExact = ArrayDup v10 PatchPoint NoSingletonClass(Array@0x1008) PatchPoint MethodRedefined(Array@0x1008, map@0x1010, cme:0x1018) - v21:BasicObject = CCallWithFrame v11, :Array#map@0x1040, block=0x1048 + v20:BasicObject = SendDirect v11, 0x1040, :map (0x1050) CheckInterrupts - Return v21 + Return v20 "); } @@ -12004,4 +12004,44 @@ mod hir_opt_tests { Return v99 "); } + + #[test] + fn test_array_each() { + eval("[1, 2, 3].each { |x| x }"); + assert_snapshot!(hir_string_proc("Array.instance_method(:each)"), @r" + fn each@: + bb0(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + v2:NilClass = Const Value(nil) + Jump bb2(v1, v2) + bb1(v5:BasicObject): + EntryPoint JIT(0) + v6:NilClass = Const Value(nil) + Jump bb2(v5, v6) + bb2(v8:BasicObject, v9:NilClass): + v13:NilClass = Const Value(nil) + v15:TrueClass|NilClass = Defined yield, v13 + v17:CBool = Test v15 + IfFalse v17, bb3(v8, v9) + v35:Fixnum[0] = Const Value(0) + Jump bb7(v8, v35) + bb3(v23:BasicObject, v24:NilClass): + v28:BasicObject = InvokeBuiltin , v23 + CheckInterrupts + Return v28 + bb7(v48:BasicObject, v49:BasicObject): + v52:BasicObject = InvokeBuiltin rb_jit_ary_at_end, v48, v49 + v54:CBool = Test v52 + IfFalse v54, bb6(v48, v49) + CheckInterrupts + Return v48 + bb6(v67:BasicObject, v68:BasicObject): + v72:BasicObject = InvokeBuiltin rb_jit_ary_at, v67, v68 + v74:BasicObject = InvokeBlock, v72 # SendFallbackReason: Uncategorized(invokeblock) + v78:BasicObject = InvokeBuiltin rb_jit_fixnum_inc, v67, v68 + PatchPoint NoEPEscape(each) + Jump bb7(v67, v78) + "); + } } diff --git a/zjit/src/hir/tests.rs b/zjit/src/hir/tests.rs index f3ab0fa57cfb71..8830b199c4330e 100644 --- a/zjit/src/hir/tests.rs +++ b/zjit/src/hir/tests.rs @@ -4069,6 +4069,54 @@ pub mod hir_build_tests { SideExit TooManyKeywordParameters "); } + + #[test] + fn test_array_each() { + assert_snapshot!(hir_string_proc("Array.instance_method(:each)"), @r" + fn each@: + bb0(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + v2:NilClass = Const Value(nil) + Jump bb2(v1, v2) + bb1(v5:BasicObject): + EntryPoint JIT(0) + v6:NilClass = Const Value(nil) + Jump bb2(v5, v6) + bb2(v8:BasicObject, v9:NilClass): + v13:NilClass = Const Value(nil) + v15:TrueClass|NilClass = Defined yield, v13 + v17:CBool = Test v15 + v18:NilClass = RefineType v15, Falsy + IfFalse v17, bb3(v8, v9) + v20:TrueClass = RefineType v15, Truthy + Jump bb5(v8, v9) + bb3(v23:BasicObject, v24:NilClass): + v28:BasicObject = InvokeBuiltin , v23 + Jump bb4(v23, v24, v28) + bb4(v40:BasicObject, v41:NilClass, v42:BasicObject): + CheckInterrupts + Return v42 + bb5(v30:BasicObject, v31:NilClass): + v35:Fixnum[0] = Const Value(0) + Jump bb7(v30, v35) + bb7(v48:BasicObject, v49:BasicObject): + v52:BasicObject = InvokeBuiltin rb_jit_ary_at_end, v48, v49 + v54:CBool = Test v52 + v55:Falsy = RefineType v52, Falsy + IfFalse v54, bb6(v48, v49) + v57:Truthy = RefineType v52, Truthy + v59:NilClass = Const Value(nil) + CheckInterrupts + Return v48 + bb6(v67:BasicObject, v68:BasicObject): + v72:BasicObject = InvokeBuiltin rb_jit_ary_at, v67, v68 + v74:BasicObject = InvokeBlock, v72 # SendFallbackReason: Uncategorized(invokeblock) + v78:BasicObject = InvokeBuiltin rb_jit_fixnum_inc, v67, v68 + PatchPoint NoEPEscape(each) + Jump bb7(v67, v78) + "); + } } /// Test successor and predecessor set computations. diff --git a/zjit/src/state.rs b/zjit/src/state.rs index a807be3f12d4c9..04411e7efbac4e 100644 --- a/zjit/src/state.rs +++ b/zjit/src/state.rs @@ -1,7 +1,7 @@ //! Runtime state of ZJIT. use crate::codegen::{gen_entry_trampoline, gen_exit_trampoline, gen_exit_trampoline_with_counter, gen_function_stub_hit_trampoline}; -use crate::cruby::{self, rb_bug_panic_hook, rb_vm_insn_count, src_loc, EcPtr, Qnil, Qtrue, rb_vm_insn_addr2opcode, rb_profile_frames, VALUE, VM_INSTRUCTION_SIZE, size_t, rb_gc_mark, with_vm_lock}; +use crate::cruby::{self, rb_bug_panic_hook, rb_vm_insn_count, src_loc, EcPtr, Qnil, Qtrue, rb_vm_insn_addr2opcode, rb_profile_frames, VALUE, VM_INSTRUCTION_SIZE, size_t, rb_gc_mark, with_vm_lock, rust_str_to_id, rb_funcallv, rb_const_get, rb_cRubyVM}; use crate::cruby_methods; use crate::invariants::Invariants; use crate::asm::CodeBlock; @@ -319,7 +319,11 @@ pub extern "C" fn rb_zjit_init(zjit_enabled: bool) { /// Enable ZJIT compilation. fn zjit_enable() { - // TODO: call RubyVM::ZJIT::call_jit_hooks here + // Call ZJIT hooks before enabling ZJIT to avoid compiling the hooks themselves + unsafe { + let zjit = rb_const_get(rb_cRubyVM, rust_str_to_id("ZJIT")); + rb_funcallv(zjit, rust_str_to_id("call_jit_hooks"), 0, std::ptr::null()); + } // Catch panics to avoid UB for unwinding into C frames. // See https://doc.rust-lang.org/nomicon/exception-safety.html From 11c845efecc76735110e40d9b05ff5045f1ce925 Mon Sep 17 00:00:00 2001 From: Max Bernstein Date: Fri, 6 Feb 2026 21:43:23 -0500 Subject: [PATCH 8/9] ZJIT: Inline Primitives for Array#each --- array.c | 6 +++--- zjit/src/cruby_methods.rs | 42 +++++++++++++++++++++++++++++++++++++++ zjit/src/hir/opt_tests.rs | 25 +++++++++++++++-------- zjit/src/hir/tests.rs | 12 +++++------ 4 files changed, 68 insertions(+), 17 deletions(-) diff --git a/array.c b/array.c index 876642dff71f94..c652c3c04491a0 100644 --- a/array.c +++ b/array.c @@ -2695,21 +2695,21 @@ ary_enum_length(VALUE ary, VALUE args, VALUE eobj) } // Return true if the index is at or past the end of the array. -static VALUE +VALUE rb_jit_ary_at_end(rb_execution_context_t *ec, VALUE self, VALUE index) { return FIX2LONG(index) >= RARRAY_LEN(self) ? Qtrue : Qfalse; } // Return the element at the given fixnum index. -static VALUE +VALUE rb_jit_ary_at(rb_execution_context_t *ec, VALUE self, VALUE index) { return RARRAY_AREF(self, FIX2LONG(index)); } // Increment a fixnum by 1. -static VALUE +VALUE rb_jit_fixnum_inc(rb_execution_context_t *ec, VALUE self, VALUE num) { return LONG2FIX(FIX2LONG(num) + 1); diff --git a/zjit/src/cruby_methods.rs b/zjit/src/cruby_methods.rs index aeca0e36bd7783..56ff3ca1aa377a 100644 --- a/zjit/src/cruby_methods.rs +++ b/zjit/src/cruby_methods.rs @@ -14,6 +14,13 @@ use std::ffi::c_void; use crate::hir_type::{types, Type}; use crate::hir; +// Array iteration builtin functions (defined in array.c) +unsafe extern "C" { + fn rb_jit_ary_at_end(ec: EcPtr, self_: VALUE, index: VALUE) -> VALUE; + fn rb_jit_ary_at(ec: EcPtr, self_: VALUE, index: VALUE) -> VALUE; + fn rb_jit_fixnum_inc(ec: EcPtr, self_: VALUE, num: VALUE) -> VALUE; +} + pub struct Annotations { cfuncs: HashMap<*mut c_void, FnProperties>, builtin_funcs: HashMap<*mut c_void, FnProperties>, @@ -270,6 +277,11 @@ pub fn init() -> Annotations { annotate_builtin!(rb_cSymbol, "name", types::StringExact); annotate_builtin!(rb_cSymbol, "to_s", types::StringExact); + // Array iteration builtins (used in with_jit Array#each, map, select, find) + builtin_funcs.insert(rb_jit_fixnum_inc as *mut c_void, FnProperties { inline: inline_fixnum_inc, return_type: types::Fixnum, ..Default::default() }); + builtin_funcs.insert(rb_jit_ary_at as *mut c_void, FnProperties { inline: inline_ary_at, ..Default::default() }); + builtin_funcs.insert(rb_jit_ary_at_end as *mut c_void, FnProperties { inline: inline_ary_at_end, return_type: types::BoolExact, ..Default::default() }); + Annotations { cfuncs: std::mem::take(cfuncs), builtin_funcs: std::mem::take(builtin_funcs), @@ -892,3 +904,33 @@ fn inline_kernel_class(fun: &mut hir::Function, block: hir::BlockId, _recv: hir: let real_class = unsafe { rb_class_real(recv_class) }; Some(fun.push_insn(block, hir::Insn::Const { val: hir::Const::Value(real_class) })) } + +/// Inline `fixnum_inc(ec, self, num)` implies FixnumAdd(num, 1). +/// num is always a Fixnum (starts at 0 and is incremented by fixnum_inc). +fn inline_fixnum_inc(fun: &mut hir::Function, block: hir::BlockId, _recv: hir::InsnId, args: &[hir::InsnId], state: hir::InsnId) -> Option { + let &[_self, num] = args else { return None; }; + let one = fun.push_insn(block, hir::Insn::Const { val: hir::Const::Value(VALUE::fixnum_from_usize(1)) }); + let result = fun.push_insn(block, hir::Insn::FixnumAdd { left: num, right: one, state }); + Some(result) +} + +/// Inline `ary_at(ec, self, index)` implies ArrayAref. +/// Called from Array#each etc. where self is Array and index is in bounds. +fn inline_ary_at(fun: &mut hir::Function, block: hir::BlockId, _recv: hir::InsnId, args: &[hir::InsnId], _state: hir::InsnId) -> Option { + let &[recv, index] = args else { return None; }; + let recv = fun.push_insn(block, hir::Insn::RefineType { val: recv, new_type: types::Array }); + let index = fun.push_insn(block, hir::Insn::UnboxFixnum { val: index }); + let result = fun.push_insn(block, hir::Insn::ArrayAref { array: recv, index }); + Some(result) +} + +/// Inline `ary_at_end(ec, self, index)` implies index >= ArrayLength(self). +/// Called from Array#each etc. where self is Array and index is Fixnum. +fn inline_ary_at_end(fun: &mut hir::Function, block: hir::BlockId, _recv: hir::InsnId, args: &[hir::InsnId], state: hir::InsnId) -> Option { + let &[recv, index] = args else { return None; }; + let recv = fun.push_insn(block, hir::Insn::RefineType { val: recv, new_type: types::Array }); + let length_cint = fun.push_insn(block, hir::Insn::ArrayLength { array: recv }); + let length = fun.push_insn(block, hir::Insn::BoxFixnum { val: length_cint, state }); + let result = fun.push_insn(block, hir::Insn::FixnumGe { left: index, right: length }); + Some(result) +} diff --git a/zjit/src/hir/opt_tests.rs b/zjit/src/hir/opt_tests.rs index 8a172e6b128f10..d32de872d52563 100644 --- a/zjit/src/hir/opt_tests.rs +++ b/zjit/src/hir/opt_tests.rs @@ -12030,18 +12030,27 @@ mod hir_opt_tests { v28:BasicObject = InvokeBuiltin , v23 CheckInterrupts Return v28 - bb7(v48:BasicObject, v49:BasicObject): - v52:BasicObject = InvokeBuiltin rb_jit_ary_at_end, v48, v49 - v54:CBool = Test v52 + bb7(v48:BasicObject, v49:Fixnum): + v83:Array = RefineType v48, Array + v84:CInt64 = ArrayLength v83 + v85:Fixnum = BoxFixnum v84 + v86:BoolExact = FixnumGe v49, v85 + IncrCounter inline_cfunc_optimized_send_count + v54:CBool = Test v86 IfFalse v54, bb6(v48, v49) CheckInterrupts Return v48 - bb6(v67:BasicObject, v68:BasicObject): - v72:BasicObject = InvokeBuiltin rb_jit_ary_at, v67, v68 - v74:BasicObject = InvokeBlock, v72 # SendFallbackReason: Uncategorized(invokeblock) - v78:BasicObject = InvokeBuiltin rb_jit_fixnum_inc, v67, v68 + bb6(v67:BasicObject, v68:Fixnum): + v88:Array = RefineType v67, Array + v89:CInt64 = UnboxFixnum v68 + v90:BasicObject = ArrayAref v88, v89 + IncrCounter inline_cfunc_optimized_send_count + v74:BasicObject = InvokeBlock, v90 # SendFallbackReason: Uncategorized(invokeblock) + v92:Fixnum[1] = Const Value(1) + v93:Fixnum = FixnumAdd v68, v92 + IncrCounter inline_cfunc_optimized_send_count PatchPoint NoEPEscape(each) - Jump bb7(v67, v78) + Jump bb7(v67, v93) "); } } diff --git a/zjit/src/hir/tests.rs b/zjit/src/hir/tests.rs index 8830b199c4330e..0401ffcd7d6738 100644 --- a/zjit/src/hir/tests.rs +++ b/zjit/src/hir/tests.rs @@ -4100,19 +4100,19 @@ pub mod hir_build_tests { bb5(v30:BasicObject, v31:NilClass): v35:Fixnum[0] = Const Value(0) Jump bb7(v30, v35) - bb7(v48:BasicObject, v49:BasicObject): - v52:BasicObject = InvokeBuiltin rb_jit_ary_at_end, v48, v49 + bb7(v48:BasicObject, v49:Fixnum): + v52:BoolExact = InvokeBuiltin rb_jit_ary_at_end, v48, v49 v54:CBool = Test v52 - v55:Falsy = RefineType v52, Falsy + v55:FalseClass = RefineType v52, Falsy IfFalse v54, bb6(v48, v49) - v57:Truthy = RefineType v52, Truthy + v57:TrueClass = RefineType v52, Truthy v59:NilClass = Const Value(nil) CheckInterrupts Return v48 - bb6(v67:BasicObject, v68:BasicObject): + bb6(v67:BasicObject, v68:Fixnum): v72:BasicObject = InvokeBuiltin rb_jit_ary_at, v67, v68 v74:BasicObject = InvokeBlock, v72 # SendFallbackReason: Uncategorized(invokeblock) - v78:BasicObject = InvokeBuiltin rb_jit_fixnum_inc, v67, v68 + v78:Fixnum = InvokeBuiltin rb_jit_fixnum_inc, v67, v68 PatchPoint NoEPEscape(each) Jump bb7(v67, v78) "); From f96d106d3a94661a4cc39c0285f78fe84ed2f891 Mon Sep 17 00:00:00 2001 From: Max Bernstein Date: Mon, 9 Feb 2026 18:55:32 -0500 Subject: [PATCH 9/9] Don't use cexpr --- array.c | 8 ++++++++ array.rb | 6 +++--- 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/array.c b/array.c index c652c3c04491a0..ac7b73c0dddf49 100644 --- a/array.c +++ b/array.c @@ -2715,6 +2715,14 @@ rb_jit_fixnum_inc(rb_execution_context_t *ec, VALUE self, VALUE num) return LONG2FIX(FIX2LONG(num) + 1); } +// Push a value onto an array and return the value. +VALUE +rb_jit_ary_push(rb_execution_context_t *ec, VALUE self, VALUE ary, VALUE val) +{ + rb_ary_push(ary, val); + return val; +} + /* * call-seq: * each {|element| ... } -> self diff --git a/array.rb b/array.rb index e21de1a5b2496e..7ee4e09a4c00eb 100644 --- a/array.rb +++ b/array.rb @@ -244,8 +244,8 @@ def map # :nodoc: i = 0 result = Primitive.ary_sized_alloc until Primitive.rb_jit_ary_at_end(i) - _value = yield(Primitive.rb_jit_ary_at(i)) - Primitive.cexpr!(%q{ rb_ary_push(result, _value) }) + value = yield(Primitive.rb_jit_ary_at(i)) + Primitive.rb_jit_ary_push(result, value) i = Primitive.rb_jit_fixnum_inc(i) end result @@ -272,7 +272,7 @@ def select # :nodoc: until Primitive.rb_jit_ary_at_end(i) value = Primitive.rb_jit_ary_at(i) if yield value - Primitive.cexpr!(%q{ rb_ary_push(result, value) }) + Primitive.rb_jit_ary_push(result, value) end i = Primitive.rb_jit_fixnum_inc(i) end