summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMax Bernstein <ruby@bernsteinbear.com>2025-11-20 10:28:23 -0500
committerMax Bernstein <tekknolagi@gmail.com>2025-11-20 08:32:17 -0800
commitf8cb9f320ceddbfe6feffb6410bb6212ed27673c (patch)
tree33b49e19dc796fdadd93d5cface1302da8361a22
parent0b6daad624e36d755f7a6919af2c2eee11353da1 (diff)
ZJIT: Put optional interpreter cache on both GetIvar and SetIvar
-rw-r--r--vm_insnhelper.c6
-rw-r--r--zjit/src/codegen.rs32
-rw-r--r--zjit/src/cruby.rs1
-rw-r--r--zjit/src/hir.rs35
-rw-r--r--zjit/src/hir/opt_tests.rs2
-rw-r--r--zjit/src/hir/tests.rs2
6 files changed, 40 insertions, 38 deletions
diff --git a/vm_insnhelper.c b/vm_insnhelper.c
index 7626d46135..ff686d047a 100644
--- a/vm_insnhelper.c
+++ b/vm_insnhelper.c
@@ -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)
{
diff --git a/zjit/src/codegen.rs b/zjit/src/codegen.rs
index 84b3bbd136..8838a72fc8 100644
--- a/zjit/src/codegen.rs
+++ b/zjit/src/codegen.rs
@@ -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 {
diff --git a/zjit/src/cruby.rs b/zjit/src/cruby.rs
index 61c25a4092..a854f2e07c 100644
--- a/zjit/src/cruby.rs
+++ b/zjit/src/cruby.rs
@@ -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;
diff --git a/zjit/src/hir.rs b/zjit/src/hir.rs
index 5db44025b9..bbe5dd3435 100644
--- a/zjit/src/hir.rs
+++ b/zjit/src/hir.rs
@@ -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());
diff --git a/zjit/src/hir/opt_tests.rs b/zjit/src/hir/opt_tests.rs
index 19f0e91b47..70efa00076 100644
--- a/zjit/src/hir/opt_tests.rs
+++ b/zjit/src/hir/opt_tests.rs
@@ -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
");
diff --git a/zjit/src/hir/tests.rs b/zjit/src/hir/tests.rs
index a00ca97e85..5e6ec11892 100644
--- a/zjit/src/hir/tests.rs
+++ b/zjit/src/hir/tests.rs
@@ -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
");