Skip to content
Merged
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
Align load attr miss cooldown with CPython
  • Loading branch information
youknowone committed Mar 10, 2026
commit f7924ddf38f487d093900b1b816c3e36adc48802
86 changes: 53 additions & 33 deletions crates/vm/src/frame.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7357,19 +7357,38 @@ impl ExecutingFrame<'_> {
return;
}

// Module attribute access: use LoadAttrModule
if obj.downcast_ref_if_exact::<PyModule>(_vm).is_some() {
unsafe {
self.code
.instructions
.write_cache_u32(cache_base + 1, type_version);
let attr_name = self.code.names[oparg.name_idx() as usize];

// Match CPython: only specialize module attribute loads when the
// current module dict has no __getattr__ override and the attribute is
// already present.
if let Some(module) = obj.downcast_ref_if_exact::<PyModule>(_vm) {
let module_dict = module.dict();
match (
module_dict.get_item_opt(identifier!(_vm, __getattr__), _vm),
module_dict.get_item_opt(attr_name, _vm),
) {
(Ok(None), Ok(Some(_))) => {
unsafe {
self.code
.instructions
.write_cache_u32(cache_base + 1, type_version);
}
self.specialize_at(instr_idx, cache_base, Instruction::LoadAttrModule);
}
Comment thread
coderabbitai[bot] marked this conversation as resolved.
(Ok(_), Ok(_)) => self.cooldown_adaptive_at(cache_base),
_ => unsafe {
self.code.instructions.write_adaptive_counter(
cache_base,
bytecode::adaptive_counter_backoff(
self.code.instructions.read_adaptive_counter(cache_base),
),
);
},
}
self.specialize_at(instr_idx, cache_base, Instruction::LoadAttrModule);
return;
}

let attr_name = self.code.names[oparg.name_idx() as usize];

// Look up attr in class via MRO
let cls_attr = cls.get_attr(attr_name);
let class_has_dict = cls.slots.flags.has_feature(PyTypeFlags::HAS_DICT);
Expand Down Expand Up @@ -7479,8 +7498,11 @@ impl ExecutingFrame<'_> {
Instruction::LoadAttrNondescriptorWithValues,
);
} else {
// No class attr, must be in instance dict
let use_hint = if let Some(dict) = obj.dict() {
// Match CPython ABSENT/no-shadow behavior: if the
// attribute is missing on both the class and the current
// instance, keep the generic opcode and just enter
// cooldown instead of specializing a repeated miss path.
let has_instance_attr = if let Some(dict) = obj.dict() {
match dict.get_item_opt(attr_name, _vm) {
Ok(Some(_)) => true,
Ok(None) => false,
Expand All @@ -7501,20 +7523,16 @@ impl ExecutingFrame<'_> {
} else {
false
};
unsafe {
self.code
.instructions
.write_cache_u32(cache_base + 1, type_version);
if has_instance_attr {
unsafe {
self.code
.instructions
.write_cache_u32(cache_base + 1, type_version);
}
self.specialize_at(instr_idx, cache_base, Instruction::LoadAttrWithHint);
} else {
self.cooldown_adaptive_at(cache_base);
}
self.specialize_at(
instr_idx,
cache_base,
if use_hint {
Instruction::LoadAttrWithHint
} else {
Instruction::LoadAttrInstanceValue
},
);
}
} else if let Some(ref descr) = cls_attr {
// No dict support, plain class attr — cache directly
Expand All @@ -7528,15 +7546,8 @@ impl ExecutingFrame<'_> {
Instruction::LoadAttrNondescriptorNoDict,
);
} else {
// No dict, no class attr — can't specialize
unsafe {
self.code.instructions.write_adaptive_counter(
cache_base,
bytecode::adaptive_counter_backoff(
self.code.instructions.read_adaptive_counter(cache_base),
),
);
}
// No dict and no class attr: repeated miss path, so cooldown.
self.cooldown_adaptive_at(cache_base);
}
}
}
Expand Down Expand Up @@ -7932,6 +7943,15 @@ impl ExecutingFrame<'_> {
}
}

#[inline]
fn cooldown_adaptive_at(&mut self, cache_base: usize) {
unsafe {
self.code
.instructions
.write_adaptive_counter(cache_base, ADAPTIVE_COOLDOWN_VALUE);
}
}

/// Commit a specialization result: replace op on success, backoff on failure.
#[inline]
fn commit_specialization(
Expand Down