Skip to content

Commit a0c02a4

Browse files
committed
ZJIT: Optimize loading instance variables in one go
Skip intermediate GetIvar and optimize straight through, either from getinstancevariable opcode or from methods with METHOD_TYPE_IVAR.
1 parent b2e8843 commit a0c02a4

4 files changed

Lines changed: 184 additions & 172 deletions

File tree

zjit/src/hir.rs

Lines changed: 51 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -3894,14 +3894,25 @@ impl Function {
38943894
}
38953895

38963896
self.push_insn(block, Insn::PatchPoint { invariant: Invariant::MethodRedefined { klass, method: mid, cme }, state });
3897+
3898+
let id = unsafe { get_cme_def_body_attr_id(cme) };
38973899
if let Some(profiled_type) = profiled_type {
38983900
let argc = unsafe { vm_ci_argc(ci) } as i32;
38993901
recv = self.push_insn(block, Insn::GuardType { val: recv, guard_type: Type::from_profiled_type(profiled_type), state, recompile: Some(Recompile::ProfileSend{ argc }) });
3900-
}
3901-
let id = unsafe { get_cme_def_body_attr_id(cme) };
39023902

3903-
let getivar = self.push_insn(block, Insn::GetIvar { self_val: recv, id, ic: std::ptr::null(), state });
3904-
self.make_equal_to(insn_id, getivar);
3903+
let replacement = self.try_emit_optimized_getivar(block, recv, id, profiled_type, state).unwrap_or_else(|counter| {
3904+
self.count(block, counter);
3905+
self.push_insn(block, Insn::GetIvar { self_val: recv, id, ic: std::ptr::null(), state })
3906+
});
3907+
self.make_equal_to(insn_id, replacement);
3908+
} else {
3909+
// No shape information, just static class information
3910+
let resolution = self.resolve_receiver_type_from_profile(recv, state);
3911+
let counter = Self::getivar_fallback_reason(resolution, std::ptr::null());
3912+
self.count(block, counter);
3913+
let getivar = self.push_insn(block, Insn::GetIvar { self_val: recv, id, ic: std::ptr::null(), state });
3914+
self.make_equal_to(insn_id, getivar);
3915+
}
39053916
} else if let (false, VM_METHOD_TYPE_ATTRSET, &[val]) = (has_block, def_type, args.as_slice()) {
39063917
// Check if we're accessing ivars of a Class or Module object as they require single-ractor mode.
39073918
// We omit gen_prepare_non_leaf_call on gen_getivar, so it's unsafe to raise for multi-ractor mode.
@@ -4904,43 +4915,35 @@ impl Function {
49044915
}
49054916
}
49064917

4918+
fn try_emit_optimized_getivar(&mut self, block: BlockId, self_val: InsnId, id: ID, profiled_type: ProfiledType, state: InsnId) -> Result<InsnId, Counter> {
4919+
if profiled_type.flags().is_immediate() {
4920+
// Instance variable lookups on immediate values are always nil
4921+
return Err(Counter::getivar_fallback_immediate);
4922+
}
4923+
assert!(profiled_type.shape().is_valid());
4924+
if profiled_type.shape().is_complex() {
4925+
// too-complex shapes can't use index access
4926+
return Err(Counter::getivar_fallback_complex);
4927+
}
4928+
if self.policy.no_side_exits {
4929+
// On the final version, skip GetIvar shape specialization.
4930+
// iseq_to_hir already generates polymorphic branches with a
4931+
// GetIvar C call fallback for getinstancevariable, so we don't
4932+
// need to wrap it again here.
4933+
return Err(Counter::getivar_fallback_no_side_exits);
4934+
}
4935+
let self_val = self.guard_heap(block, self_val, state);
4936+
let shape = self.load_shape(block, self_val);
4937+
self.guard_shape(block, shape, profiled_type.shape(), state, Some(Recompile::ProfileSelf));
4938+
Ok(self.load_ivar(block, self_val, profiled_type, id))
4939+
}
4940+
49074941
fn optimize_getivar(&mut self) {
49084942
for block in self.reverse_post_order() {
49094943
let old_insns = std::mem::take(&mut self.blocks[block.0].insns);
49104944
assert!(self.blocks[block.0].insns.is_empty());
49114945
for insn_id in old_insns {
49124946
match self.find(insn_id) {
4913-
Insn::GetIvar { self_val, id, ic, state } => {
4914-
let Some(recv_type) = self.profiled_type_of_at(self_val, state) else {
4915-
let resolution = self.resolve_receiver_type_from_profile(self_val, state);
4916-
let counter = Self::getivar_fallback_reason(resolution, ic);
4917-
self.count(block, counter);
4918-
self.push_insn_id(block, insn_id); continue;
4919-
};
4920-
if recv_type.flags().is_immediate() {
4921-
// Instance variable lookups on immediate values are always nil
4922-
self.count(block, Counter::getivar_fallback_immediate);
4923-
self.push_insn_id(block, insn_id); continue;
4924-
}
4925-
assert!(recv_type.shape().is_valid());
4926-
if recv_type.shape().is_complex() {
4927-
// too-complex shapes can't use index access
4928-
self.count(block, Counter::getivar_fallback_complex);
4929-
self.push_insn_id(block, insn_id); continue;
4930-
}
4931-
if self.policy.no_side_exits {
4932-
// On the final version, skip GetIvar shape specialization.
4933-
// iseq_to_hir already generates polymorphic branches with a
4934-
// GetIvar C call fallback for getinstancevariable, so we don't
4935-
// need to wrap it again here.
4936-
self.push_insn_id(block, insn_id); continue;
4937-
}
4938-
let self_val = self.guard_heap(block, self_val, state);
4939-
let shape = self.load_shape(block, self_val);
4940-
self.guard_shape(block, shape, recv_type.shape(), state, Some(Recompile::ProfileSelf));
4941-
let replacement = self.load_ivar(block, self_val, recv_type, id);
4942-
self.make_equal_to(insn_id, replacement);
4943-
}
49444947
Insn::SetIvar { self_val, id, val, state, ic } => {
49454948
let Some(recv_type) = self.profiled_type_of_at(self_val, state) else {
49464949
// No (monomorphic/skewed polymorphic) profile info
@@ -9045,9 +9048,19 @@ fn add_iseq_to_hir(
90459048
// make the right number of Params
90469049
block = join_block;
90479050
} else {
9048-
// Possibly monomorphic case; handled in optimize_getivar
9049-
let result = fun.push_insn(block, Insn::GetIvar { self_val: self_param, id, ic, state: exit_id });
9050-
state.stack_push(result);
9051+
if let Some(profiled_type) = fun.monomorphic_summary(&profiles, self_param, exit_id) {
9052+
let result = fun.try_emit_optimized_getivar(block, self_param, id, profiled_type, exit_id).unwrap_or_else(|counter| {
9053+
fun.count(block, counter);
9054+
fun.push_insn(block, Insn::GetIvar { self_val: self_param, id, ic: std::ptr::null(), state: exit_id })
9055+
});
9056+
state.stack_push(result);
9057+
} else {
9058+
let resolution = fun.resolve_receiver_type_from_profile(self_param, exit_id);
9059+
let counter = Function::getivar_fallback_reason(resolution, ic);
9060+
fun.count(block, counter);
9061+
let result = fun.push_insn(block, Insn::GetIvar { self_val: self_param, id, ic, state: exit_id });
9062+
state.stack_push(result);
9063+
}
90519064
}
90529065
}
90539066
YARVINSN_setinstancevariable => {

0 commit comments

Comments
 (0)