mirror of
https://github.com/ruby/ruby.git
synced 2026-01-27 04:24:23 +00:00
ZJIT: Put optional interpreter cache on both GetIvar and SetIvar
This commit is contained in:
parent
0b6daad624
commit
f8cb9f320c
Notes:
git
2025-11-20 16:32:49 +00:00
@ -1723,6 +1723,12 @@ rb_vm_setinstancevariable(const rb_iseq_t *iseq, VALUE obj, ID id, VALUE val, IV
|
||||
vm_setinstancevariable(iseq, obj, id, val, ic);
|
||||
}
|
||||
|
||||
VALUE
|
||||
rb_vm_getinstancevariable(const rb_iseq_t *iseq, VALUE obj, ID id, IVC ic)
|
||||
{
|
||||
return vm_getinstancevariable(iseq, obj, id, ic);
|
||||
}
|
||||
|
||||
static VALUE
|
||||
vm_throw_continue(const rb_execution_context_t *ec, VALUE err)
|
||||
{
|
||||
|
||||
@ -428,7 +428,7 @@ fn gen_insn(cb: &mut CodeBlock, jit: &mut JITState, asm: &mut Assembler, functio
|
||||
Insn::CCallVariadic { cfunc, recv, args, name, cme, state, return_type: _, elidable: _ } => {
|
||||
gen_ccall_variadic(jit, asm, *cfunc, *name, opnd!(recv), opnds!(args), *cme, &function.frame_state(*state))
|
||||
}
|
||||
Insn::GetIvar { self_val, id, state: _ } => gen_getivar(asm, opnd!(self_val), *id),
|
||||
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))),
|
||||
Insn::GetGlobal { id, state } => gen_getglobal(jit, asm, *id, &function.frame_state(*state)),
|
||||
&Insn::GetLocal { ep_offset, level, use_sp, .. } => gen_getlocal(asm, ep_offset, level, use_sp),
|
||||
@ -436,8 +436,7 @@ fn gen_insn(cb: &mut CodeBlock, jit: &mut JITState, asm: &mut Assembler, functio
|
||||
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))),
|
||||
Insn::SetIvar { self_val, id, val, state } => no_output!(gen_setivar(jit, asm, opnd!(self_val), *id, opnd!(val), &function.frame_state(*state))),
|
||||
Insn::SetInstanceVariable { self_val, id, ic, val, state } => no_output!(gen_set_instance_variable(jit, asm, opnd!(self_val), *id, *ic, opnd!(val), &function.frame_state(*state))),
|
||||
Insn::SetIvar { self_val, id, ic, val, state } => no_output!(gen_setivar(jit, asm, opnd!(self_val), *id, *ic, opnd!(val), &function.frame_state(*state))),
|
||||
Insn::FixnumBitCheck { val, index } => gen_fixnum_bit_check(asm, opnd!(val), *index),
|
||||
Insn::SideExit { state, reason } => no_output!(gen_side_exit(jit, asm, reason, &function.frame_state(*state))),
|
||||
Insn::PutSpecialObject { value_type } => gen_putspecialobject(asm, *value_type),
|
||||
@ -881,26 +880,27 @@ fn gen_ccall_variadic(
|
||||
}
|
||||
|
||||
/// Emit an uncached instance variable lookup
|
||||
fn gen_getivar(asm: &mut Assembler, recv: Opnd, id: ID) -> Opnd {
|
||||
fn gen_getivar(jit: &mut JITState, asm: &mut Assembler, recv: Opnd, id: ID, ic: *const iseq_inline_iv_cache_entry) -> Opnd {
|
||||
gen_incr_counter(asm, Counter::dynamic_getivar_count);
|
||||
asm_ccall!(asm, rb_ivar_get, recv, id.0.into())
|
||||
if ic.is_null() {
|
||||
asm_ccall!(asm, rb_ivar_get, recv, id.0.into())
|
||||
} else {
|
||||
let iseq = Opnd::Value(jit.iseq.into());
|
||||
asm_ccall!(asm, rb_vm_getinstancevariable, iseq, recv, id.0.into(), Opnd::const_ptr(ic))
|
||||
}
|
||||
}
|
||||
|
||||
/// Emit an uncached instance variable store
|
||||
fn gen_setivar(jit: &mut JITState, asm: &mut Assembler, recv: Opnd, id: ID, val: Opnd, state: &FrameState) {
|
||||
fn gen_setivar(jit: &mut JITState, asm: &mut Assembler, recv: Opnd, id: ID, ic: *const iseq_inline_iv_cache_entry, val: Opnd, state: &FrameState) {
|
||||
gen_incr_counter(asm, Counter::dynamic_setivar_count);
|
||||
// Setting an ivar can raise FrozenError, so we need proper frame state for exception handling.
|
||||
gen_prepare_non_leaf_call(jit, asm, state);
|
||||
asm_ccall!(asm, rb_ivar_set, recv, id.0.into(), val);
|
||||
}
|
||||
|
||||
/// Emit an uncached instance variable store using the interpreter inline cache
|
||||
fn gen_set_instance_variable(jit: &mut JITState, asm: &mut Assembler, recv: Opnd, id: ID, ic: *const iseq_inline_iv_cache_entry, val: Opnd, state: &FrameState) {
|
||||
gen_incr_counter(asm, Counter::dynamic_setivar_count);
|
||||
// Setting an ivar can raise FrozenError, so we need proper frame state for exception handling.
|
||||
gen_prepare_non_leaf_call(jit, asm, state);
|
||||
let iseq = Opnd::Value(jit.iseq.into());
|
||||
asm_ccall!(asm, rb_vm_setinstancevariable, iseq, recv, id.0.into(), val, Opnd::const_ptr(ic));
|
||||
if ic.is_null() {
|
||||
asm_ccall!(asm, rb_ivar_set, recv, id.0.into(), val);
|
||||
} else {
|
||||
let iseq = Opnd::Value(jit.iseq.into());
|
||||
asm_ccall!(asm, rb_vm_setinstancevariable, iseq, recv, id.0.into(), val, Opnd::const_ptr(ic));
|
||||
}
|
||||
}
|
||||
|
||||
fn gen_getclassvar(jit: &mut JITState, asm: &mut Assembler, id: ID, ic: *const iseq_inline_cvar_cache_entry, state: &FrameState) -> Opnd {
|
||||
|
||||
@ -147,6 +147,7 @@ unsafe extern "C" {
|
||||
) -> bool;
|
||||
pub fn rb_vm_set_ivar_id(obj: VALUE, idx: u32, val: VALUE) -> VALUE;
|
||||
pub fn rb_vm_setinstancevariable(iseq: IseqPtr, obj: VALUE, id: ID, val: VALUE, ic: IVC);
|
||||
pub fn rb_vm_getinstancevariable(iseq: IseqPtr, obj: VALUE, id: ID, ic: IVC) -> VALUE;
|
||||
pub fn rb_aliased_callable_method_entry(
|
||||
me: *const rb_callable_method_entry_t,
|
||||
) -> *const rb_callable_method_entry_t;
|
||||
|
||||
@ -707,12 +707,10 @@ pub enum Insn {
|
||||
SetGlobal { id: ID, val: InsnId, state: InsnId },
|
||||
|
||||
//NewObject?
|
||||
/// Get an instance variable `id` from `self_val`
|
||||
GetIvar { self_val: InsnId, id: ID, state: InsnId },
|
||||
/// Set `self_val`'s instance variable `id` to `val`
|
||||
SetIvar { self_val: InsnId, id: ID, val: InsnId, state: InsnId },
|
||||
/// Set `self_val`'s instance variable `id` to `val` using the interpreter inline cache
|
||||
SetInstanceVariable { self_val: InsnId, id: ID, ic: *const iseq_inline_iv_cache_entry, val: InsnId, state: InsnId },
|
||||
/// Get an instance variable `id` from `self_val`, using the inline cache `ic` if present
|
||||
GetIvar { self_val: InsnId, id: ID, ic: *const iseq_inline_iv_cache_entry, state: InsnId },
|
||||
/// Set `self_val`'s instance variable `id` to `val`, using the inline cache `ic` if present
|
||||
SetIvar { self_val: InsnId, id: ID, val: InsnId, ic: *const iseq_inline_iv_cache_entry, state: InsnId },
|
||||
/// Check whether an instance variable exists on `self_val`
|
||||
DefinedIvar { self_val: InsnId, id: ID, pushval: VALUE, state: InsnId },
|
||||
|
||||
@ -912,7 +910,7 @@ impl Insn {
|
||||
| Insn::PatchPoint { .. } | Insn::SetIvar { .. } | Insn::SetClassVar { .. } | Insn::ArrayExtend { .. }
|
||||
| Insn::ArrayPush { .. } | Insn::SideExit { .. } | Insn::SetGlobal { .. }
|
||||
| Insn::SetLocal { .. } | Insn::Throw { .. } | Insn::IncrCounter(_) | Insn::IncrCounterPtr { .. }
|
||||
| Insn::CheckInterrupts { .. } | Insn::GuardBlockParamProxy { .. } | Insn::SetInstanceVariable { .. } | Insn::StoreField { .. } | Insn::WriteBarrier { .. } => false,
|
||||
| Insn::CheckInterrupts { .. } | Insn::GuardBlockParamProxy { .. } | Insn::StoreField { .. } | Insn::WriteBarrier { .. } => false,
|
||||
_ => true,
|
||||
}
|
||||
}
|
||||
@ -1248,7 +1246,6 @@ impl<'a> std::fmt::Display for InsnPrinter<'a> {
|
||||
&Insn::StoreField { recv, id, offset, val } => write!(f, "StoreField {recv}, :{}@{:p}, {val}", id.contents_lossy(), self.ptr_map.map_offset(offset)),
|
||||
&Insn::WriteBarrier { recv, val } => write!(f, "WriteBarrier {recv}, {val}"),
|
||||
Insn::SetIvar { self_val, id, val, .. } => write!(f, "SetIvar {self_val}, :{}, {val}", id.contents_lossy()),
|
||||
Insn::SetInstanceVariable { self_val, id, val, .. } => write!(f, "SetInstanceVariable {self_val}, :{}, {val}", id.contents_lossy()),
|
||||
Insn::GetGlobal { id, .. } => write!(f, "GetGlobal :{}", id.contents_lossy()),
|
||||
Insn::SetGlobal { id, val, .. } => write!(f, "SetGlobal :{}, {val}", id.contents_lossy()),
|
||||
&Insn::GetLocal { level, ep_offset, use_sp: true, rest_param } => write!(f, "GetLocal l{level}, SP@{}{}", ep_offset + 1, if rest_param { ", *" } else { "" }),
|
||||
@ -1891,12 +1888,11 @@ impl Function {
|
||||
&ArrayInclude { ref elements, target, state } => ArrayInclude { elements: find_vec!(elements), target: find!(target), state: find!(state) },
|
||||
&DupArrayInclude { ary, target, state } => DupArrayInclude { ary, target: find!(target), state: find!(state) },
|
||||
&SetGlobal { id, val, state } => SetGlobal { id, val: find!(val), state },
|
||||
&GetIvar { self_val, id, state } => GetIvar { self_val: find!(self_val), id, state },
|
||||
&GetIvar { self_val, id, ic, state } => GetIvar { self_val: find!(self_val), id, ic, state },
|
||||
&LoadField { recv, id, offset, return_type } => LoadField { recv: find!(recv), id, offset, return_type },
|
||||
&StoreField { recv, id, offset, val } => StoreField { recv: find!(recv), id, offset, val: find!(val) },
|
||||
&WriteBarrier { recv, val } => WriteBarrier { recv: find!(recv), val: find!(val) },
|
||||
&SetIvar { self_val, id, val, state } => SetIvar { self_val: find!(self_val), id, val: find!(val), state },
|
||||
&SetInstanceVariable { self_val, id, ic, val, state } => SetInstanceVariable { self_val: find!(self_val), id, ic, val: find!(val), state },
|
||||
&SetIvar { self_val, id, ic, val, state } => SetIvar { self_val: find!(self_val), id, ic, val: find!(val), state },
|
||||
&GetClassVar { id, ic, state } => GetClassVar { id, ic, state },
|
||||
&SetClassVar { id, val, ic, state } => SetClassVar { id, val: find!(val), ic, state },
|
||||
&SetLocal { val, ep_offset, level } => SetLocal { val: find!(val), ep_offset, level },
|
||||
@ -1951,7 +1947,7 @@ impl Function {
|
||||
| Insn::PatchPoint { .. } | Insn::SetIvar { .. } | Insn::SetClassVar { .. } | Insn::ArrayExtend { .. }
|
||||
| Insn::ArrayPush { .. } | Insn::SideExit { .. } | Insn::SetLocal { .. } | Insn::IncrCounter(_)
|
||||
| Insn::CheckInterrupts { .. } | Insn::GuardBlockParamProxy { .. } | Insn::IncrCounterPtr { .. }
|
||||
| Insn::SetInstanceVariable { .. } | Insn::StoreField { .. } | Insn::WriteBarrier { .. } =>
|
||||
| Insn::StoreField { .. } | Insn::WriteBarrier { .. } =>
|
||||
panic!("Cannot infer type of instruction with no output: {}. See Insn::has_output().", self.insns[insn.0]),
|
||||
Insn::Const { val: Const::Value(val) } => Type::from_value(*val),
|
||||
Insn::Const { val: Const::CBool(val) } => Type::from_cbool(*val),
|
||||
@ -2450,7 +2446,7 @@ impl Function {
|
||||
self.push_insn(block, Insn::PatchPoint { invariant: Invariant::SingleRactorMode, state });
|
||||
}
|
||||
}
|
||||
let getivar = self.push_insn(block, Insn::GetIvar { self_val: recv, id, state });
|
||||
let getivar = self.push_insn(block, Insn::GetIvar { self_val: recv, id, ic: std::ptr::null(), state });
|
||||
self.make_equal_to(insn_id, getivar);
|
||||
} else if let (VM_METHOD_TYPE_ATTRSET, &[val]) = (def_type, args.as_slice()) {
|
||||
self.push_insn(block, Insn::PatchPoint { invariant: Invariant::MethodRedefined { klass, method: mid, cme }, state });
|
||||
@ -2467,7 +2463,7 @@ impl Function {
|
||||
self.push_insn(block, Insn::PatchPoint { invariant: Invariant::SingleRactorMode, state });
|
||||
}
|
||||
}
|
||||
self.push_insn(block, Insn::SetIvar { self_val: recv, id, val, state });
|
||||
self.push_insn(block, Insn::SetIvar { self_val: recv, id, ic: std::ptr::null(), val, state });
|
||||
self.make_equal_to(insn_id, val);
|
||||
} else if def_type == VM_METHOD_TYPE_OPTIMIZED {
|
||||
let opt_type: OptimizedMethodType = unsafe { get_cme_def_body_optimized_type(cme) }.into();
|
||||
@ -2750,7 +2746,7 @@ impl Function {
|
||||
assert!(self.blocks[block.0].insns.is_empty());
|
||||
for insn_id in old_insns {
|
||||
match self.find(insn_id) {
|
||||
Insn::GetIvar { self_val, id, state } => {
|
||||
Insn::GetIvar { self_val, id, ic: _, state } => {
|
||||
let frame_state = self.frame_state(state);
|
||||
let Some(recv_type) = self.profiled_type_of_at(self_val, frame_state.insn_idx) else {
|
||||
// No (monomorphic/skewed polymorphic) profile info
|
||||
@ -3503,8 +3499,7 @@ impl Function {
|
||||
worklist.push_back(self_val);
|
||||
worklist.push_back(state);
|
||||
}
|
||||
&Insn::SetIvar { self_val, val, state, .. }
|
||||
| &Insn::SetInstanceVariable { self_val, val, state, .. } => {
|
||||
&Insn::SetIvar { self_val, val, state, .. } => {
|
||||
worklist.push_back(self_val);
|
||||
worklist.push_back(val);
|
||||
worklist.push_back(state);
|
||||
@ -4082,7 +4077,6 @@ impl Function {
|
||||
}
|
||||
// Instructions with 2 Ruby object operands
|
||||
Insn::SetIvar { self_val: left, val: right, .. }
|
||||
| Insn::SetInstanceVariable { self_val: left, val: right, .. }
|
||||
| Insn::NewRange { low: left, high: right, .. }
|
||||
| Insn::AnyToString { val: left, str: right, .. }
|
||||
| Insn::WriteBarrier { recv: left, val: right } => {
|
||||
@ -5450,11 +5444,12 @@ pub fn iseq_to_hir(iseq: *const rb_iseq_t) -> Result<Function, ParseError> {
|
||||
}
|
||||
YARVINSN_getinstancevariable => {
|
||||
let id = ID(get_arg(pc, 0).as_u64());
|
||||
let ic = get_arg(pc, 1).as_ptr();
|
||||
// ic is in arg 1
|
||||
// Assume single-Ractor mode to omit gen_prepare_non_leaf_call on gen_getivar
|
||||
// TODO: We only really need this if self_val is a class/module
|
||||
fun.push_insn(block, Insn::PatchPoint { invariant: Invariant::SingleRactorMode, state: exit_id });
|
||||
let result = fun.push_insn(block, Insn::GetIvar { self_val: self_param, id, state: exit_id });
|
||||
let result = fun.push_insn(block, Insn::GetIvar { self_val: self_param, id, ic, state: exit_id });
|
||||
state.stack_push(result);
|
||||
}
|
||||
YARVINSN_setinstancevariable => {
|
||||
@ -5464,7 +5459,7 @@ pub fn iseq_to_hir(iseq: *const rb_iseq_t) -> Result<Function, ParseError> {
|
||||
// TODO: We only really need this if self_val is a class/module
|
||||
fun.push_insn(block, Insn::PatchPoint { invariant: Invariant::SingleRactorMode, state: exit_id });
|
||||
let val = state.stack_pop()?;
|
||||
fun.push_insn(block, Insn::SetInstanceVariable { self_val: self_param, id, ic, val, state: exit_id });
|
||||
fun.push_insn(block, Insn::SetIvar { self_val: self_param, id, ic, val, state: exit_id });
|
||||
}
|
||||
YARVINSN_getclassvariable => {
|
||||
let id = ID(get_arg(pc, 0).as_u64());
|
||||
|
||||
@ -3367,7 +3367,7 @@ mod hir_opt_tests {
|
||||
bb2(v6:BasicObject):
|
||||
v10:Fixnum[1] = Const Value(1)
|
||||
PatchPoint SingleRactorMode
|
||||
SetInstanceVariable v6, :@foo, v10
|
||||
SetIvar v6, :@foo, v10
|
||||
CheckInterrupts
|
||||
Return v10
|
||||
");
|
||||
|
||||
@ -2241,7 +2241,7 @@ pub mod hir_build_tests {
|
||||
bb2(v6:BasicObject):
|
||||
v10:Fixnum[1] = Const Value(1)
|
||||
PatchPoint SingleRactorMode
|
||||
SetInstanceVariable v6, :@foo, v10
|
||||
SetIvar v6, :@foo, v10
|
||||
CheckInterrupts
|
||||
Return v10
|
||||
");
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user