Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
687e99f
Add debug_assert to invoke_exact_args, lazy func_version reassignment
youknowone Mar 2, 2026
81d307b
working
youknowone Mar 1, 2026
0176223
Add COMPARE_OP, TO_BOOL, FOR_ITER, LOAD_GLOBAL specialization
youknowone Mar 1, 2026
9bb0c46
Add BINARY_SUBSCR, CONTAINS_OP, UNPACK_SEQUENCE, STORE_ATTR specializ…
youknowone Mar 1, 2026
1c07777
Add STORE_SUBSCR, BinaryOpAddUnicode, ToBoolAlwaysTrue, CallLen, Call…
youknowone Mar 1, 2026
240f3ac
Add BinaryOpSubscrStrInt, CallStr1, CallTuple1 specialization
youknowone Mar 1, 2026
cadb9be
Add BinaryOpInplaceAddUnicode specialization
youknowone Mar 1, 2026
fd098fe
Add LoadAttrModule, CallBuiltinO, CallPyGeneral, CallBoundMethodGener…
youknowone Mar 2, 2026
dd29113
Add LoadAttrNondescriptor*, CallMethodDescriptor* specialization
youknowone Mar 2, 2026
b238a27
Add CallBuiltinFast, CallNonPyGeneral specialization
youknowone Mar 2, 2026
d950035
Add SendGen specialization for generator/coroutine send
youknowone Mar 2, 2026
32376d5
Add LoadAttrSlot, StoreAttrSlot specialization for __slots__ access
youknowone Mar 2, 2026
a7c179c
Add LoadSuperAttrAttr, LoadSuperAttrMethod, CallBuiltinClass, CallBui…
youknowone Mar 2, 2026
e1289f1
Add LoadAttrProperty specialization for property descriptor access
youknowone Mar 2, 2026
2350bc1
Add LoadAttrClass specialization for class attribute access
youknowone Mar 2, 2026
ba9d528
Add BinaryOpSubscrListSlice specialization
youknowone Mar 2, 2026
3c88368
Add CallKwPy, CallKwBoundMethod, CallKwNonPy specialization
youknowone Mar 2, 2026
ab6bbb6
Clean up comments in specialization code
youknowone Mar 2, 2026
48fd5c7
fix check_signals
youknowone Mar 2, 2026
51accdb
fix import
youknowone Mar 3, 2026
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
Next Next commit
Add debug_assert to invoke_exact_args, lazy func_version reassignment
- Add debug_assert preconditions in invoke_exact_args
- Add get_version_for_current_state() for lazy version reassignment
  after func_version invalidation
- Document NEXT_TYPE_VERSION overflow policy
  • Loading branch information
youknowone committed Mar 3, 2026
commit 687e99f02b119b16d57b44fa470a732e44fe1c4e
26 changes: 26 additions & 0 deletions crates/vm/src/builtins/function.rs
Original file line number Diff line number Diff line change
Expand Up @@ -603,6 +603,22 @@ impl Py<PyFunction> {
self.func_version.load(Relaxed)
}

/// _PyFunction_GetVersionForCurrentState
/// Returns the current version, assigning a fresh one if previously invalidated.
/// Returns 0 if the version counter has overflowed.
pub fn get_version_for_current_state(&self) -> u32 {
let v = self.func_version.load(Relaxed);
if v != 0 {
return v;
}
let new_v = FUNC_VERSION_COUNTER.fetch_add(1, Relaxed);
if new_v == 0 {
return 0; // Counter overflow
}
self.func_version.store(new_v, Relaxed);
new_v
Comment thread
coderabbitai[bot] marked this conversation as resolved.
}

/// Check if this function is eligible for exact-args call specialization.
/// Returns true if: no VARARGS, no VARKEYWORDS, no kwonly args, not generator/coroutine,
/// and effective_nargs matches co_argcount.
Expand All @@ -627,6 +643,16 @@ impl Py<PyFunction> {
pub fn invoke_exact_args(&self, args: &[PyObjectRef], vm: &VirtualMachine) -> PyResult {
let code: PyRef<PyCode> = (*self.code).to_owned();

debug_assert_eq!(args.len(), code.arg_count as usize);
debug_assert!(code.flags.contains(bytecode::CodeFlags::NEWLOCALS));
debug_assert!(!code.flags.intersects(
bytecode::CodeFlags::VARARGS
| bytecode::CodeFlags::VARKEYWORDS
| bytecode::CodeFlags::GENERATOR
| bytecode::CodeFlags::COROUTINE
));
debug_assert_eq!(code.kwonlyarg_count, 0);

let frame = Frame::new(
code.clone(),
Scope::new(None, self.globals.clone()),
Expand Down
7 changes: 6 additions & 1 deletion crates/vm/src/builtins/type.rs
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,10 @@ pub struct PyType {
pub tp_version_tag: AtomicU32,
}

/// Monotonic counter for type version tags. Once it reaches `u32::MAX`,
/// `assign_version_tag()` returns 0 permanently, disabling new inline-cache
/// entries but not invalidating correctness (cache misses fall back to the
/// generic path).
static NEXT_TYPE_VERSION: AtomicU32 = AtomicU32::new(1);

unsafe impl crate::object::Traverse for PyType {
Expand Down Expand Up @@ -199,7 +203,8 @@ fn is_subtype_with_mro(a_mro: &[PyTypeRef], a: &Py<PyType>, b: &Py<PyType>) -> b
}

impl PyType {
/// Assign a fresh version tag. Returns 0 on overflow (all caches invalidated).
/// Assign a fresh version tag. Returns 0 if the version counter has been
/// exhausted, in which case no new cache entries can be created.
pub fn assign_version_tag(&self) -> u32 {
loop {
let current = NEXT_TYPE_VERSION.load(Ordering::Relaxed);
Expand Down
2 changes: 1 addition & 1 deletion crates/vm/src/frame.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4765,7 +4765,7 @@ impl ExecutingFrame<'_> {
let callable = self.nth_value(nargs + 1);

if let Some(func) = callable.downcast_ref::<PyFunction>() {
let version = func.func_version();
let version = func.get_version_for_current_state();
if version == 0 {
unsafe {
self.code
Expand Down