Skip to content

Commit 1cca3ef

Browse files
authored
ZJIT: Use interpreter inline cache in setinstancevariable (ruby#14925)
We have both `SetIvar` and `SetInstanceVariable`. The former is a purely dynamic fallback that we can inline `attr_accessor`/`attr_writer` into, whereas the latter comes straight from the interpreter's `setinstancevariable` opcode.
1 parent f7e7443 commit 1cca3ef

File tree

4 files changed

+23
-7
lines changed

4 files changed

+23
-7
lines changed

zjit/src/codegen.rs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -426,6 +426,7 @@ fn gen_insn(cb: &mut CodeBlock, jit: &mut JITState, asm: &mut Assembler, functio
426426
Insn::GetClassVar { id, ic, state } => gen_getclassvar(jit, asm, *id, *ic, &function.frame_state(*state)),
427427
Insn::SetClassVar { id, val, ic, state } => no_output!(gen_setclassvar(jit, asm, *id, opnd!(val), *ic, &function.frame_state(*state))),
428428
Insn::SetIvar { self_val, id, val, state } => no_output!(gen_setivar(jit, asm, opnd!(self_val), *id, opnd!(val), &function.frame_state(*state))),
429+
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))),
429430
Insn::SideExit { state, reason } => no_output!(gen_side_exit(jit, asm, reason, &function.frame_state(*state))),
430431
Insn::PutSpecialObject { value_type } => gen_putspecialobject(asm, *value_type),
431432
Insn::AnyToString { val, str, state } => gen_anytostring(asm, opnd!(val), opnd!(str), &function.frame_state(*state)),
@@ -840,6 +841,15 @@ fn gen_setivar(jit: &mut JITState, asm: &mut Assembler, recv: Opnd, id: ID, val:
840841
asm_ccall!(asm, rb_ivar_set, recv, id.0.into(), val);
841842
}
842843

844+
/// Emit an uncached instance variable store using the interpreter inline cache
845+
fn gen_set_instance_variable(jit: &mut JITState, asm: &mut Assembler, recv: Opnd, id: ID, ic: *const iseq_inline_constant_cache, val: Opnd, state: &FrameState) {
846+
gen_incr_counter(asm, Counter::dynamic_setivar_count);
847+
// Setting an ivar can raise FrozenError, so we need proper frame state for exception handling.
848+
gen_prepare_non_leaf_call(jit, asm, state);
849+
let iseq = Opnd::Value(jit.iseq.into());
850+
asm_ccall!(asm, rb_vm_setinstancevariable, iseq, recv, id.0.into(), val, Opnd::const_ptr(ic));
851+
}
852+
843853
fn gen_getclassvar(jit: &mut JITState, asm: &mut Assembler, id: ID, ic: *const iseq_inline_cvar_cache_entry, state: &FrameState) -> Opnd {
844854
gen_prepare_non_leaf_call(jit, asm, state);
845855
asm_ccall!(asm, rb_vm_getclassvariable, VALUE::from(jit.iseq).into(), CFP, id.0.into(), Opnd::const_ptr(ic))

zjit/src/hir.rs

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -675,6 +675,8 @@ pub enum Insn {
675675
GetIvar { self_val: InsnId, id: ID, state: InsnId },
676676
/// Set `self_val`'s instance variable `id` to `val`
677677
SetIvar { self_val: InsnId, id: ID, val: InsnId, state: InsnId },
678+
/// Set `self_val`'s instance variable `id` to `val` using the interpreter inline cache
679+
SetInstanceVariable { self_val: InsnId, id: ID, ic: *const iseq_inline_constant_cache, val: InsnId, state: InsnId },
678680
/// Check whether an instance variable exists on `self_val`
679681
DefinedIvar { self_val: InsnId, id: ID, pushval: VALUE, state: InsnId },
680682

@@ -866,7 +868,7 @@ impl Insn {
866868
| Insn::PatchPoint { .. } | Insn::SetIvar { .. } | Insn::SetClassVar { .. } | Insn::ArrayExtend { .. }
867869
| Insn::ArrayPush { .. } | Insn::SideExit { .. } | Insn::SetGlobal { .. }
868870
| Insn::SetLocal { .. } | Insn::Throw { .. } | Insn::IncrCounter(_) | Insn::IncrCounterPtr { .. }
869-
| Insn::CheckInterrupts { .. } | Insn::GuardBlockParamProxy { .. } => false,
871+
| Insn::CheckInterrupts { .. } | Insn::GuardBlockParamProxy { .. } | Insn::SetInstanceVariable { .. } => false,
870872
_ => true,
871873
}
872874
}
@@ -1193,6 +1195,7 @@ impl<'a> std::fmt::Display for InsnPrinter<'a> {
11931195
Insn::LoadSelf => write!(f, "LoadSelf"),
11941196
&Insn::LoadField { recv, id, offset, return_type: _ } => write!(f, "LoadField {recv}, :{}@{:p}", id.contents_lossy(), self.ptr_map.map_offset(offset)),
11951197
Insn::SetIvar { self_val, id, val, .. } => write!(f, "SetIvar {self_val}, :{}, {val}", id.contents_lossy()),
1198+
Insn::SetInstanceVariable { self_val, id, val, .. } => write!(f, "SetInstanceVariable {self_val}, :{}, {val}", id.contents_lossy()),
11961199
Insn::GetGlobal { id, .. } => write!(f, "GetGlobal :{}", id.contents_lossy()),
11971200
Insn::SetGlobal { id, val, .. } => write!(f, "SetGlobal :{}, {val}", id.contents_lossy()),
11981201
&Insn::GetLocal { level, ep_offset, use_sp: true, rest_param } => write!(f, "GetLocal l{level}, SP@{}{}", ep_offset + 1, if rest_param { ", *" } else { "" }),
@@ -1817,6 +1820,7 @@ impl Function {
18171820
&GetIvar { self_val, id, state } => GetIvar { self_val: find!(self_val), id, state },
18181821
&LoadField { recv, id, offset, return_type } => LoadField { recv: find!(recv), id, offset, return_type },
18191822
&SetIvar { self_val, id, val, state } => SetIvar { self_val: find!(self_val), id, val: find!(val), state },
1823+
&SetInstanceVariable { self_val, id, ic, val, state } => SetInstanceVariable { self_val: find!(self_val), id, ic, val: find!(val), state },
18201824
&GetClassVar { id, ic, state } => GetClassVar { id, ic, state },
18211825
&SetClassVar { id, val, ic, state } => SetClassVar { id, val: find!(val), ic, state },
18221826
&SetLocal { val, ep_offset, level } => SetLocal { val: find!(val), ep_offset, level },
@@ -1870,7 +1874,8 @@ impl Function {
18701874
| Insn::IfTrue { .. } | Insn::IfFalse { .. } | Insn::Return { .. } | Insn::Throw { .. }
18711875
| Insn::PatchPoint { .. } | Insn::SetIvar { .. } | Insn::SetClassVar { .. } | Insn::ArrayExtend { .. }
18721876
| Insn::ArrayPush { .. } | Insn::SideExit { .. } | Insn::SetLocal { .. } | Insn::IncrCounter(_)
1873-
| Insn::CheckInterrupts { .. } | Insn::GuardBlockParamProxy { .. } | Insn::IncrCounterPtr { .. } =>
1877+
| Insn::CheckInterrupts { .. } | Insn::GuardBlockParamProxy { .. } | Insn::IncrCounterPtr { .. }
1878+
| Insn::SetInstanceVariable { .. } =>
18741879
panic!("Cannot infer type of instruction with no output: {}", self.insns[insn.0]),
18751880
Insn::Const { val: Const::Value(val) } => Type::from_value(*val),
18761881
Insn::Const { val: Const::CBool(val) } => Type::from_cbool(*val),
@@ -3379,7 +3384,8 @@ impl Function {
33793384
worklist.push_back(self_val);
33803385
worklist.push_back(state);
33813386
}
3382-
&Insn::SetIvar { self_val, val, state, .. } => {
3387+
&Insn::SetIvar { self_val, val, state, .. }
3388+
| &Insn::SetInstanceVariable { self_val, val, state, .. } => {
33833389
worklist.push_back(self_val);
33843390
worklist.push_back(val);
33853391
worklist.push_back(state);
@@ -5089,13 +5095,13 @@ pub fn iseq_to_hir(iseq: *const rb_iseq_t) -> Result<Function, ParseError> {
50895095
}
50905096
YARVINSN_setinstancevariable => {
50915097
let id = ID(get_arg(pc, 0).as_u64());
5092-
// ic is in arg 1
5098+
let ic = get_arg(pc, 1).as_ptr();
50935099
let exit_id = fun.push_insn(block, Insn::Snapshot { state: exit_state });
50945100
// Assume single-Ractor mode to omit gen_prepare_non_leaf_call on gen_setivar
50955101
// TODO: We only really need this if self_val is a class/module
50965102
fun.push_insn(block, Insn::PatchPoint { invariant: Invariant::SingleRactorMode, state: exit_id });
50975103
let val = state.stack_pop()?;
5098-
fun.push_insn(block, Insn::SetIvar { self_val: self_param, id, val, state: exit_id });
5104+
fun.push_insn(block, Insn::SetInstanceVariable { self_val: self_param, id, ic, val, state: exit_id });
50995105
}
51005106
YARVINSN_getclassvariable => {
51015107
let id = ID(get_arg(pc, 0).as_u64());

zjit/src/hir/opt_tests.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3198,7 +3198,7 @@ mod hir_opt_tests {
31983198
bb2(v6:BasicObject):
31993199
v10:Fixnum[1] = Const Value(1)
32003200
PatchPoint SingleRactorMode
3201-
SetIvar v6, :@foo, v10
3201+
SetInstanceVariable v6, :@foo, v10
32023202
CheckInterrupts
32033203
Return v10
32043204
");

zjit/src/hir/tests.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2185,7 +2185,7 @@ pub mod hir_build_tests {
21852185
bb2(v6:BasicObject):
21862186
v10:Fixnum[1] = Const Value(1)
21872187
PatchPoint SingleRactorMode
2188-
SetIvar v6, :@foo, v10
2188+
SetInstanceVariable v6, :@foo, v10
21892189
CheckInterrupts
21902190
Return v10
21912191
");

0 commit comments

Comments
 (0)