From ec2b2b77248ff5ba44e8d644adb74c6ceb3a803d Mon Sep 17 00:00:00 2001 From: Takashi Kokubun Date: Thu, 26 Mar 2026 20:12:56 -0700 Subject: ZJIT: Replace blockiseq: Option with block: Option Introduce a BlockHandler enum to represent how a block is passed to a send-like instruction. For now it has only the BlockIseq(IseqPtr) variant, making this a pure mechanical rename with no behavior change. --- zjit/src/codegen.rs | 28 ++++++++-------- zjit/src/hir.rs | 93 +++++++++++++++++++++++++++++------------------------ 2 files changed, 65 insertions(+), 56 deletions(-) (limited to 'zjit') diff --git a/zjit/src/codegen.rs b/zjit/src/codegen.rs index 3a6d60af2d..db0477f7c9 100644 --- a/zjit/src/codegen.rs +++ b/zjit/src/codegen.rs @@ -21,7 +21,7 @@ use crate::stats::{counter_ptr, with_time_stat, Counter, Counter::{compile_time_ use crate::{asm::CodeBlock, cruby::*, options::debug, virtualmem::CodePtr}; use crate::backend::lir::{self, Assembler, C_ARG_OPNDS, C_RET_OPND, CFP, EC, NATIVE_STACK_PTR, Opnd, SP, SideExit, SideExitRecompile, Target, asm_ccall, asm_comment}; use crate::hir::{iseq_to_hir, BlockId, Invariant, RangeType, SideExitReason::{self, *}, SpecialBackrefSymbol, SpecialObjectType}; -use crate::hir::{Const, FrameState, Function, Insn, InsnId, SendFallbackReason}; +use crate::hir::{BlockHandler, Const, FrameState, Function, Insn, InsnId, SendFallbackReason}; use crate::hir_type::{types, Type}; use crate::options::{get_option, PerfMap}; use crate::cast::IntoUsize; @@ -615,10 +615,10 @@ fn gen_insn(cb: &mut CodeBlock, jit: &mut JITState, asm: &mut Assembler, functio Insn::Param => unreachable!("block.insns should not have Insn::Param"), Insn::LoadArg { .. } => return Ok(()), // compiled in the LoadArg pre-pass above Insn::Snapshot { .. } => return Ok(()), // we don't need to do anything for this instruction at the moment - &Insn::Send { cd, blockiseq: None, state, reason, .. } => gen_send_without_block(jit, asm, cd, &function.frame_state(state), reason), - &Insn::Send { cd, blockiseq: Some(blockiseq), state, reason, .. } => gen_send(jit, asm, cd, blockiseq, &function.frame_state(state), reason), + &Insn::Send { cd, block: None, state, reason, .. } => gen_send_without_block(jit, asm, cd, &function.frame_state(state), reason), + &Insn::Send { cd, block: Some(BlockHandler::BlockIseq(blockiseq)), state, reason, .. } => gen_send(jit, asm, cd, blockiseq, &function.frame_state(state), reason), &Insn::SendForward { cd, blockiseq, state, reason, .. } => gen_send_forward(jit, asm, cd, blockiseq, &function.frame_state(state), reason), - Insn::SendDirect { cme, iseq, recv, args, kw_bits, blockiseq, state, .. } => gen_send_iseq_direct(cb, jit, asm, *cme, *iseq, opnd!(recv), opnds!(args), *kw_bits, &function.frame_state(*state), *blockiseq), + Insn::SendDirect { cme, iseq, recv, args, kw_bits, block, state, .. } => gen_send_iseq_direct(cb, jit, asm, *cme, *iseq, opnd!(recv), opnds!(args), *kw_bits, &function.frame_state(*state), *block), &Insn::InvokeSuper { cd, blockiseq, state, reason, .. } => gen_invokesuper(jit, asm, cd, blockiseq, &function.frame_state(state), reason), &Insn::InvokeSuperForward { cd, blockiseq, state, reason, .. } => gen_invokesuperforward(jit, asm, cd, blockiseq, &function.frame_state(state), reason), &Insn::InvokeBlock { cd, state, reason, .. } => gen_invokeblock(jit, asm, cd, &function.frame_state(state), reason), @@ -680,10 +680,10 @@ fn gen_insn(cb: &mut CodeBlock, jit: &mut JITState, asm: &mut Assembler, functio // There's no test case for this because no core cfuncs have this many parameters. But C extensions could have such methods. Insn::CCallWithFrame { cd, state, args, .. } if args.len() + 1 > C_ARG_OPNDS.len() => gen_send_without_block(jit, asm, *cd, &function.frame_state(*state), SendFallbackReason::CCallWithFrameTooManyArgs), - Insn::CCallWithFrame { cfunc, recv, name, args, cme, state, blockiseq, .. } => - gen_ccall_with_frame(jit, asm, *cfunc, *name, opnd!(recv), opnds!(args), *cme, *blockiseq, &function.frame_state(*state)), - Insn::CCallVariadic { cfunc, recv, name, args, cme, state, blockiseq, return_type: _, elidable: _ } => { - gen_ccall_variadic(jit, asm, *cfunc, *name, opnd!(recv), opnds!(args), *cme, *blockiseq, &function.frame_state(*state)) + Insn::CCallWithFrame { cfunc, recv, name, args, cme, state, block, .. } => + gen_ccall_with_frame(jit, asm, *cfunc, *name, opnd!(recv), opnds!(args), *cme, *block, &function.frame_state(*state)), + Insn::CCallVariadic { cfunc, recv, name, args, cme, state, block, return_type: _, elidable: _ } => { + gen_ccall_variadic(jit, asm, *cfunc, *name, opnd!(recv), opnds!(args), *cme, *block, &function.frame_state(*state)) } Insn::GetIvar { self_val, id, ic, state: _ } => gen_getivar(jit, asm, opnd!(self_val), *id, *ic), Insn::SetGlobal { id, val, state } => no_output!(gen_setglobal(jit, asm, *id, opnd!(val), &function.frame_state(*state))), @@ -1010,7 +1010,7 @@ fn gen_ccall_with_frame( recv: Opnd, args: Vec, cme: *const rb_callable_method_entry_t, - blockiseq: Option, + block: Option, state: &FrameState, ) -> lir::Opnd { gen_incr_counter(asm, Counter::non_variadic_cfunc_optimized_send_count); @@ -1026,7 +1026,7 @@ fn gen_ccall_with_frame( gen_spill_stack(jit, asm, state); gen_spill_locals(jit, asm, state); - let block_handler_specval = if let Some(block_iseq) = blockiseq { + let block_handler_specval = if let Some(BlockHandler::BlockIseq(block_iseq)) = block { // Change cfp->block_code in the current frame. See vm_caller_setup_arg_block(). // VM_CFP_TO_CAPTURED_BLOCK then turns &cfp->self into a block handler. // rb_captured_block->code.iseq aliases with cfp->block_code. @@ -1101,7 +1101,7 @@ fn gen_ccall_variadic( recv: Opnd, args: Vec, cme: *const rb_callable_method_entry_t, - blockiseq: Option, + block: Option, state: &FrameState, ) -> lir::Opnd { gen_incr_counter(asm, Counter::variadic_cfunc_optimized_send_count); @@ -1120,7 +1120,7 @@ fn gen_ccall_variadic( gen_spill_stack(jit, asm, state); gen_spill_locals(jit, asm, state); - let block_handler_specval = if let Some(blockiseq) = blockiseq { + let block_handler_specval = if let Some(BlockHandler::BlockIseq(blockiseq)) = block { gen_block_handler_specval(asm, blockiseq) } else { VM_BLOCK_HANDLER_NONE.into() @@ -1542,7 +1542,7 @@ fn gen_send_iseq_direct( args: Vec, kw_bits: u32, state: &FrameState, - blockiseq: Option, + block: Option, ) -> lir::Opnd { gen_incr_counter(asm, Counter::iseq_optimized_send_count); @@ -1562,7 +1562,7 @@ fn gen_send_iseq_direct( // The HIR specialization guards ensure we will only reach here for literal blocks, // not &block forwarding, &:foo, etc. Thise are rejected in `type_specialize` by // `unspecializable_call_type`. - let block_handler = blockiseq.map(|b| gen_block_handler_specval(asm, b)); + let block_handler = block.map(|BlockHandler::BlockIseq(b)| gen_block_handler_specval(asm, b)); let callee_is_bmethod = VM_METHOD_TYPE_BMETHOD == unsafe { get_cme_def_type(cme) }; diff --git a/zjit/src/hir.rs b/zjit/src/hir.rs index 9f3a5a9883..2abdc693cd 100644 --- a/zjit/src/hir.rs +++ b/zjit/src/hir.rs @@ -750,6 +750,14 @@ impl Display for SendFallbackReason { } } +/// How a block is passed to a send-like instruction. +#[derive(Clone, Copy, Debug, PartialEq)] +pub enum BlockHandler { + /// Literal block ISEQ passed to the send (can be null for the `send` YARV + /// instruction when no literal block is present). + BlockIseq(IseqPtr), +} + /// An instruction in the SSA IR. The output of an instruction is referred to by the index of /// the instruction ([`InsnId`]). SSA form enables this, and [`UnionFind`] ([`Function::find`]) /// helps with editing. @@ -931,7 +939,7 @@ pub enum Insn { state: InsnId, return_type: Type, elidable: bool, - blockiseq: Option, + block: Option, }, /// Call a variadic C function with signature: func(int argc, VALUE *argv, VALUE recv) @@ -945,7 +953,7 @@ pub enum Insn { state: InsnId, return_type: Type, elidable: bool, - blockiseq: Option, + block: Option, }, /// Un-optimized fallback implementation (dynamic dispatch) for send-ish instructions @@ -953,7 +961,7 @@ pub enum Insn { Send { recv: InsnId, cd: *const rb_call_data, - blockiseq: Option, + block: Option, args: Vec, state: InsnId, reason: SendFallbackReason, @@ -1004,7 +1012,7 @@ pub enum Insn { iseq: IseqPtr, args: Vec, kw_bits: u32, - blockiseq: Option, + block: Option, state: InsnId, }, @@ -1865,18 +1873,19 @@ impl<'a> std::fmt::Display for InsnPrinter<'a> { Insn::Jump(target) => { write!(f, "Jump {target}") } Insn::IfTrue { val, target } => { write!(f, "IfTrue {val}, {target}") } Insn::IfFalse { val, target } => { write!(f, "IfFalse {val}, {target}") } - Insn::SendDirect { recv, cd, iseq, args, blockiseq, .. } => { - write!(f, "SendDirect {recv}, {:p}, :{} ({:?})", self.ptr_map.map_ptr(blockiseq), ruby_call_method_name(*cd), self.ptr_map.map_ptr(iseq))?; + Insn::SendDirect { recv, cd, iseq, args, block, .. } => { + let blockiseq = block.map(|BlockHandler::BlockIseq(iseq)| iseq); + write!(f, "SendDirect {recv}, {:p}, :{} ({:?})", self.ptr_map.map_ptr(&blockiseq), ruby_call_method_name(*cd), self.ptr_map.map_ptr(iseq))?; for arg in args { write!(f, ", {arg}")?; } Ok(()) } - Insn::Send { recv, cd, args, blockiseq, reason, .. } => { + Insn::Send { recv, cd, args, block, reason, .. } => { // For tests, we want to check HIR snippets textually. Addresses change // between runs, making tests fail. Instead, pick an arbitrary hex value to // use as a "pointer" so we can check the rest of the HIR. - if let Some(blockiseq) = *blockiseq { + if let Some(BlockHandler::BlockIseq(blockiseq)) = *block { write!(f, "Send {recv}, {:p}, :{}", self.ptr_map.map_ptr(blockiseq), ruby_call_method_name(*cd))?; } else { write!(f, "Send {recv}, :{}", ruby_call_method_name(*cd))?; @@ -1991,12 +2000,12 @@ impl<'a> std::fmt::Display for InsnPrinter<'a> { } Ok(()) }, - Insn::CCallWithFrame { cfunc, recv, args, name, cme, blockiseq, .. } => { + Insn::CCallWithFrame { cfunc, recv, args, name, cme, block, .. } => { write!(f, "CCallWithFrame {recv}, :{}@{:p}", qualified_method_name(unsafe { (**cme).owner }, *name), self.ptr_map.map_ptr(cfunc))?; for arg in args { write!(f, ", {arg}")?; } - if let Some(blockiseq) = blockiseq { + if let Some(BlockHandler::BlockIseq(blockiseq)) = block { write!(f, ", block={:p}", self.ptr_map.map_ptr(blockiseq))?; } Ok(()) @@ -2755,20 +2764,20 @@ impl Function { str: find!(str), state, }, - &SendDirect { recv, cd, cme, iseq, ref args, kw_bits, blockiseq, state } => SendDirect { + &SendDirect { recv, cd, cme, iseq, ref args, kw_bits, block, state } => SendDirect { recv: find!(recv), cd, cme, iseq, args: find_vec!(args), kw_bits, - blockiseq, + block, state, }, - &Send { recv, cd, blockiseq, ref args, state, reason } => Send { + &Send { recv, cd, block, ref args, state, reason } => Send { recv: find!(recv), cd, - blockiseq, + block, args: find_vec!(args), state, reason, @@ -2817,7 +2826,7 @@ impl Function { &ObjectAlloc { val, state } => ObjectAlloc { val: find!(val), state }, &ObjectAllocClass { class, state } => ObjectAllocClass { class, state: find!(state) }, &CCall { cfunc, recv, ref args, name, owner, return_type, elidable } => CCall { cfunc, recv: find!(recv), args: find_vec!(args), name, owner, return_type, elidable }, - &CCallWithFrame { cd, cfunc, recv, ref args, cme, name, state, return_type, elidable, blockiseq } => CCallWithFrame { + &CCallWithFrame { cd, cfunc, recv, ref args, cme, name, state, return_type, elidable, block } => CCallWithFrame { cd, cfunc, recv: find!(recv), @@ -2827,10 +2836,10 @@ impl Function { state: find!(state), return_type, elidable, - blockiseq, + block, }, - &CCallVariadic { cfunc, recv, ref args, cme, name, state, return_type, elidable, blockiseq } => CCallVariadic { - cfunc, recv: find!(recv), args: find_vec!(args), cme, name, state, return_type, elidable, blockiseq + &CCallVariadic { cfunc, recv, ref args, cme, name, state, return_type, elidable, block } => CCallVariadic { + cfunc, recv: find!(recv), args: find_vec!(args), cme, name, state, return_type, elidable, block }, &CheckMatch { target, pattern, flag, state } => CheckMatch { target: find!(target), pattern: find!(pattern), flag, state: find!(state) }, &Defined { op_type, obj, pushval, v, state } => Defined { op_type, obj, pushval, v: find!(v), state: find!(state) }, @@ -3566,12 +3575,12 @@ impl Function { assert!(self.blocks[block.0].insns.is_empty()); for insn_id in old_insns { match self.find(insn_id) { - Insn::Send { recv, blockiseq: None, args, state, cd, .. } if ruby_call_method_id(cd) == ID!(freeze) && args.is_empty() => + Insn::Send { recv, block: None, args, state, cd, .. } if ruby_call_method_id(cd) == ID!(freeze) && args.is_empty() => self.try_rewrite_freeze(block, insn_id, recv, state), - Insn::Send { recv, blockiseq: None, args, state, cd, .. } if ruby_call_method_id(cd) == ID!(minusat) && args.is_empty() => + Insn::Send { recv, block: None, args, state, cd, .. } if ruby_call_method_id(cd) == ID!(minusat) && args.is_empty() => self.try_rewrite_uminus(block, insn_id, recv, state), - Insn::Send { mut recv, cd, state, blockiseq, args, .. } => { - let has_block = blockiseq.is_some(); + Insn::Send { mut recv, cd, state, block: send_block, args, .. } => { + let has_block = send_block.is_some(); let frame_state = self.frame_state(state); let (klass, profiled_type) = match self.resolve_receiver_type(recv, self.type_of(recv), frame_state.insn_idx) { ReceiverTypeResolution::StaticallyKnown { class } => (class, None), @@ -3670,7 +3679,7 @@ impl Function { recv = self.push_insn(block, Insn::GuardType { val: recv, guard_type: Type::from_profiled_type(profiled_type), state }); } - let send_direct = self.push_insn(block, Insn::SendDirect { recv, cd, cme, iseq, args: processed_args, kw_bits, state: send_state, blockiseq }); + let send_direct = self.push_insn(block, Insn::SendDirect { recv, cd, cme, iseq, args: processed_args, kw_bits, state: send_state, block: send_block }); self.make_equal_to(insn_id, send_direct); } else if !has_block && def_type == VM_METHOD_TYPE_BMETHOD { let procv = unsafe { rb_get_def_bmethod_proc((*cme).def) }; @@ -3712,7 +3721,7 @@ impl Function { recv = self.push_insn(block, Insn::GuardType { val: recv, guard_type: Type::from_profiled_type(profiled_type), state }); } - let send_direct = self.push_insn(block, Insn::SendDirect { recv, cd, cme, iseq, args: processed_args, kw_bits, state: send_state, blockiseq: None }); + let send_direct = self.push_insn(block, Insn::SendDirect { recv, cd, cme, iseq, args: processed_args, kw_bits, state: send_state, block: None }); self.make_equal_to(insn_id, send_direct); } else if !has_block && def_type == VM_METHOD_TYPE_IVAR && args.is_empty() { // Check if we're accessing ivars of a Class or Module object as they require single-ractor mode. @@ -3877,7 +3886,7 @@ impl Function { self.make_equal_to(insn_id, guard); } else { let recv = self.push_insn(block, Insn::GuardType { val, guard_type: Type::from_profiled_type(recv_type), state}); - let send_to_s = self.push_insn(block, Insn::Send { recv, cd, blockiseq: None, args: vec![], state, reason: ObjToStringNotString }); + let send_to_s = self.push_insn(block, Insn::Send { recv, cd, block: None, args: vec![], state, reason: ObjToStringNotString }); self.make_equal_to(insn_id, send_to_s); } } @@ -4075,7 +4084,7 @@ impl Function { args: processed_args, kw_bits, state: send_state, - blockiseq: None, + block: None, }); self.make_equal_to(insn_id, send_direct); @@ -4142,7 +4151,7 @@ impl Function { state, return_type: types::BasicObject, elidable: false, - blockiseq: None, + block: None, }) }; self.make_equal_to(insn_id, ccall); @@ -4192,7 +4201,7 @@ impl Function { state, return_type: types::BasicObject, elidable: false, - blockiseq: None, + block: None, }) }; self.make_equal_to(insn_id, ccall); @@ -4588,7 +4597,7 @@ impl Function { send: Insn, send_insn_id: InsnId, ) -> Result<(), ()> { - let Insn::Send { mut recv, cd, blockiseq, args, state, .. } = send else { + let Insn::Send { mut recv, cd, block: send_block, args, state, .. } = send else { return Err(()); }; @@ -4646,10 +4655,10 @@ impl Function { return Err(()); } - let blockiseq = match blockiseq { + let blockiseq = match send_block { None => unreachable!("went to reduce_send_without_block_to_ccall"), - Some(p) if p.is_null() => None, - Some(blockiseq) => Some(blockiseq), + Some(BlockHandler::BlockIseq(p)) if p.is_null() => None, + Some(BlockHandler::BlockIseq(blockiseq)) => Some(blockiseq), }; let cfunc = unsafe { get_cme_def_body_cfunc(cme) }; @@ -4695,7 +4704,7 @@ impl Function { state, return_type: types::BasicObject, elidable: false, - blockiseq, + block: blockiseq.map(BlockHandler::BlockIseq), }); fun.make_equal_to(send_insn_id, ccall); Ok(()) @@ -4732,7 +4741,7 @@ impl Function { state, return_type: types::BasicObject, elidable: false, - blockiseq + block: blockiseq.map(BlockHandler::BlockIseq), }); fun.make_equal_to(send_insn_id, ccall); @@ -4886,7 +4895,7 @@ impl Function { state, return_type, elidable, - blockiseq: None, + block: None, }); fun.make_equal_to(send_insn_id, ccall); } @@ -4960,7 +4969,7 @@ impl Function { state, return_type, elidable, - blockiseq: None, + block: None, }); fun.make_equal_to(send_insn_id, ccall); @@ -4986,7 +4995,7 @@ impl Function { for insn_id in old_insns { let send = self.find(insn_id); match send { - send @ Insn::Send { recv, blockiseq: None, .. } => { + send @ Insn::Send { recv, block: None, .. } => { let recv_type = self.type_of(recv); if reduce_send_without_block_to_ccall(self, block, recv_type, send, insn_id).is_ok() { continue; @@ -7694,7 +7703,7 @@ pub fn iseq_to_hir(iseq: *const rb_iseq_t) -> Result { } let args = state.stack_pop_n(argc as usize)?; let recv = state.stack_pop()?; - let send = fun.push_insn(block, Insn::Send { recv, cd, blockiseq: None, args, state: exit_id, reason: Uncategorized(opcode) }); + let send = fun.push_insn(block, Insn::Send { recv, cd, block: None, args, state: exit_id, reason: Uncategorized(opcode) }); state.stack_push(send); } YARVINSN_opt_hash_freeze => { @@ -7852,7 +7861,7 @@ pub fn iseq_to_hir(iseq: *const rb_iseq_t) -> Result { let recv = state.stack_pop().unwrap(); let refined_recv = fun.push_insn(block, Insn::RefineType { val: recv, new_type }); state.replace(recv, refined_recv); - let send = fun.push_insn(block, Insn::Send { recv: refined_recv, cd, blockiseq: None, args, state: snapshot, reason: Uncategorized(opcode) }); + let send = fun.push_insn(block, Insn::Send { recv: refined_recv, cd, block: None, args, state: snapshot, reason: Uncategorized(opcode) }); state.stack_push(send); fun.push_insn(block, Insn::Jump(BranchEdge { target: join_block, args: state.as_args(self_param) })); block @@ -7891,7 +7900,7 @@ pub fn iseq_to_hir(iseq: *const rb_iseq_t) -> Result { let args = state.stack_pop_n(argc as usize)?; let recv = state.stack_pop()?; let reason = SendWithoutBlockPolymorphicFallback; - let send = fun.push_insn(block, Insn::Send { recv, cd, blockiseq: None, args, state: exit_id, reason }); + let send = fun.push_insn(block, Insn::Send { recv, cd, block: None, args, state: exit_id, reason }); state.stack_push(send); fun.push_insn(block, Insn::Jump(BranchEdge { target: join_block, args: state.as_args(self_param) })); break; // End the block @@ -7900,7 +7909,7 @@ pub fn iseq_to_hir(iseq: *const rb_iseq_t) -> Result { let args = state.stack_pop_n(argc as usize)?; let recv = state.stack_pop()?; - let send = fun.push_insn(block, Insn::Send { recv, cd, blockiseq: None, args, state: exit_id, reason: Uncategorized(opcode) }); + let send = fun.push_insn(block, Insn::Send { recv, cd, block: None, args, state: exit_id, reason: Uncategorized(opcode) }); state.stack_push(send); } YARVINSN_send => { @@ -7923,7 +7932,7 @@ pub fn iseq_to_hir(iseq: *const rb_iseq_t) -> Result { let args = state.stack_pop_n(argc as usize + usize::from(block_arg))?; let recv = state.stack_pop()?; - let send = fun.push_insn(block, Insn::Send { recv, cd, blockiseq: Some(blockiseq), args, state: exit_id, reason: Uncategorized(opcode) }); + let send = fun.push_insn(block, Insn::Send { recv, cd, block: Some(BlockHandler::BlockIseq(blockiseq)), args, state: exit_id, reason: Uncategorized(opcode) }); state.stack_push(send); if !blockiseq.is_null() { -- cgit v1.2.3