Skip to content

Commit 43794da

Browse files
committed
vm: drop call-site identity caches in generic CALL specializations
1 parent 8241b6b commit 43794da

1 file changed

Lines changed: 33 additions & 90 deletions

File tree

crates/vm/src/frame.rs

Lines changed: 33 additions & 90 deletions
Original file line numberDiff line numberDiff line change
@@ -3620,17 +3620,13 @@ impl ExecutingFrame<'_> {
36203620
self.execute_call(args, vm)
36213621
}
36223622
Instruction::CallBuiltinO => {
3623-
let instr_idx = self.lasti() as usize - 1;
3624-
let cache_base = instr_idx + 1;
3625-
let cached_tag = self.code.instructions.read_cache_u32(cache_base + 1);
36263623
let nargs: u32 = arg.into();
36273624
if nargs == 1 {
36283625
let obj = self.pop_value();
36293626
let _null = self.pop_value_opt();
36303627
let callable = self.pop_value();
3631-
let callable_tag = &*callable as *const PyObject as u32;
3632-
if cached_tag == callable_tag
3633-
&& let Some(native) = callable.downcast_ref::<PyNativeFunction>()
3628+
if let Some(native) = callable.downcast_ref::<PyNativeFunction>()
3629+
&& native.zelf.is_none()
36343630
{
36353631
let args = FuncArgs {
36363632
args: vec![obj],
@@ -3648,18 +3644,13 @@ impl ExecutingFrame<'_> {
36483644
self.execute_call(args, vm)
36493645
}
36503646
Instruction::CallBuiltinFast => {
3651-
let instr_idx = self.lasti() as usize - 1;
3652-
let cache_base = instr_idx + 1;
3653-
let cached_tag = self.code.instructions.read_cache_u32(cache_base + 1);
36543647
let nargs: u32 = arg.into();
36553648
let callable = self.nth_value(nargs + 1);
3656-
let callable_tag = callable as *const PyObject as u32;
3657-
let func = if cached_tag == callable_tag {
3649+
let func = {
36583650
callable
36593651
.downcast_ref::<PyNativeFunction>()
3652+
.filter(|n| n.zelf.is_none())
36603653
.map(|n| n.value.func)
3661-
} else {
3662-
None
36633654
};
36643655
if let Some(func) = func {
36653656
let positional_args: Vec<PyObjectRef> =
@@ -3751,21 +3742,24 @@ impl ExecutingFrame<'_> {
37513742
}
37523743
}
37533744
Instruction::CallListAppend => {
3754-
let instr_idx = self.lasti() as usize - 1;
3755-
let cache_base = instr_idx + 1;
3756-
let cached_tag = self.code.instructions.read_cache_u32(cache_base + 1);
37573745
let nargs: u32 = arg.into();
37583746
if nargs == 1 {
37593747
// Stack: [callable, self_or_null, item]
37603748
let stack = &self.state.stack;
37613749
let stack_len = stack.len();
37623750
let self_or_null_is_some = stack[stack_len - 2].is_some();
37633751
let callable = self.nth_value(2);
3764-
let callable_tag = callable as *const PyObject as u32;
37653752
let self_is_list = stack[stack_len - 2]
37663753
.as_ref()
37673754
.is_some_and(|obj| obj.downcast_ref::<PyList>().is_some());
3768-
if cached_tag == callable_tag && self_or_null_is_some && self_is_list {
3755+
let is_list_append =
3756+
callable
3757+
.downcast_ref::<PyMethodDescriptor>()
3758+
.is_some_and(|descr| {
3759+
descr.method.name == "append"
3760+
&& descr.objclass.is(vm.ctx.types.list_type)
3761+
});
3762+
if is_list_append && self_or_null_is_some && self_is_list {
37693763
let item = self.pop_value();
37703764
let self_or_null = self.pop_value_opt();
37713765
let callable = self.pop_value();
@@ -3789,18 +3783,14 @@ impl ExecutingFrame<'_> {
37893783
self.execute_call(args, vm)
37903784
}
37913785
Instruction::CallMethodDescriptorNoargs => {
3792-
let instr_idx = self.lasti() as usize - 1;
3793-
let cache_base = instr_idx + 1;
3794-
let cached_tag = self.code.instructions.read_cache_u32(cache_base + 1);
37953786
let nargs: u32 = arg.into();
37963787
if nargs == 0 {
37973788
// Stack: [callable, self_or_null] — peek to get func ptr
37983789
let stack = &self.state.stack;
37993790
let stack_len = stack.len();
38003791
let self_or_null_is_some = stack[stack_len - 1].is_some();
38013792
let callable = self.nth_value(1);
3802-
let callable_tag = callable as *const PyObject as u32;
3803-
let func = if cached_tag == callable_tag && self_or_null_is_some {
3793+
let func = if self_or_null_is_some {
38043794
callable
38053795
.downcast_ref::<PyMethodDescriptor>()
38063796
.map(|d| d.method.func)
@@ -3823,18 +3813,14 @@ impl ExecutingFrame<'_> {
38233813
self.execute_call(args, vm)
38243814
}
38253815
Instruction::CallMethodDescriptorO => {
3826-
let instr_idx = self.lasti() as usize - 1;
3827-
let cache_base = instr_idx + 1;
3828-
let cached_tag = self.code.instructions.read_cache_u32(cache_base + 1);
38293816
let nargs: u32 = arg.into();
38303817
if nargs == 1 {
38313818
// Stack: [callable, self_or_null, arg1]
38323819
let stack = &self.state.stack;
38333820
let stack_len = stack.len();
38343821
let self_or_null_is_some = stack[stack_len - 2].is_some();
38353822
let callable = self.nth_value(2);
3836-
let callable_tag = callable as *const PyObject as u32;
3837-
let func = if cached_tag == callable_tag && self_or_null_is_some {
3823+
let func = if self_or_null_is_some {
38383824
callable
38393825
.downcast_ref::<PyMethodDescriptor>()
38403826
.map(|d| d.method.func)
@@ -3858,16 +3844,12 @@ impl ExecutingFrame<'_> {
38583844
self.execute_call(args, vm)
38593845
}
38603846
Instruction::CallMethodDescriptorFast => {
3861-
let instr_idx = self.lasti() as usize - 1;
3862-
let cache_base = instr_idx + 1;
3863-
let cached_tag = self.code.instructions.read_cache_u32(cache_base + 1);
38643847
let nargs: u32 = arg.into();
38653848
let callable = self.nth_value(nargs + 1);
3866-
let callable_tag = callable as *const PyObject as u32;
38673849
let stack = &self.state.stack;
38683850
let stack_len = stack.len();
38693851
let self_or_null_is_some = stack[stack_len - nargs as usize - 1].is_some();
3870-
let func = if cached_tag == callable_tag && self_or_null_is_some {
3852+
let func = if self_or_null_is_some {
38713853
callable
38723854
.downcast_ref::<PyMethodDescriptor>()
38733855
.map(|d| d.method.func)
@@ -3894,13 +3876,9 @@ impl ExecutingFrame<'_> {
38943876
self.execute_call(args, vm)
38953877
}
38963878
Instruction::CallBuiltinClass => {
3897-
let instr_idx = self.lasti() as usize - 1;
3898-
let cache_base = instr_idx + 1;
3899-
let cached_tag = self.code.instructions.read_cache_u32(cache_base + 1);
39003879
let nargs: u32 = arg.into();
39013880
let callable = self.nth_value(nargs + 1);
3902-
let callable_tag = callable as *const PyObject as u32;
3903-
if cached_tag == callable_tag && callable.downcast_ref::<PyType>().is_some() {
3881+
if callable.downcast_ref::<PyType>().is_some() {
39043882
let args = self.collect_positional_args(nargs);
39053883
let self_or_null = self.pop_value_opt();
39063884
let callable = self.pop_value();
@@ -3978,16 +3956,12 @@ impl ExecutingFrame<'_> {
39783956
}
39793957
Instruction::CallMethodDescriptorFastWithKeywords => {
39803958
// Native function interface is uniform regardless of keyword support
3981-
let instr_idx = self.lasti() as usize - 1;
3982-
let cache_base = instr_idx + 1;
3983-
let cached_tag = self.code.instructions.read_cache_u32(cache_base + 1);
39843959
let nargs: u32 = arg.into();
39853960
let callable = self.nth_value(nargs + 1);
3986-
let callable_tag = callable as *const PyObject as u32;
39873961
let stack = &self.state.stack;
39883962
let stack_len = stack.len();
39893963
let self_or_null_is_some = stack[stack_len - nargs as usize - 1].is_some();
3990-
let func = if cached_tag == callable_tag && self_or_null_is_some {
3964+
let func = if self_or_null_is_some {
39913965
callable
39923966
.downcast_ref::<PyMethodDescriptor>()
39933967
.map(|d| d.method.func)
@@ -4015,18 +3989,13 @@ impl ExecutingFrame<'_> {
40153989
}
40163990
Instruction::CallBuiltinFastWithKeywords => {
40173991
// Native function interface is uniform regardless of keyword support
4018-
let instr_idx = self.lasti() as usize - 1;
4019-
let cache_base = instr_idx + 1;
4020-
let cached_tag = self.code.instructions.read_cache_u32(cache_base + 1);
40213992
let nargs: u32 = arg.into();
40223993
let callable = self.nth_value(nargs + 1);
4023-
let callable_tag = callable as *const PyObject as u32;
4024-
let func = if cached_tag == callable_tag {
3994+
let func = {
40253995
callable
40263996
.downcast_ref::<PyNativeFunction>()
3997+
.filter(|n| n.zelf.is_none())
40273998
.map(|n| n.value.func)
4028-
} else {
4029-
None
40303999
};
40314000
if let Some(func) = func {
40324001
let positional_args: Vec<PyObjectRef> =
@@ -4045,13 +4014,11 @@ impl ExecutingFrame<'_> {
40454014
self.execute_call(args, vm)
40464015
}
40474016
Instruction::CallNonPyGeneral => {
4048-
let instr_idx = self.lasti() as usize - 1;
4049-
let cache_base = instr_idx + 1;
4050-
let cached_tag = self.code.instructions.read_cache_u32(cache_base + 1);
40514017
let nargs: u32 = arg.into();
40524018
let callable = self.nth_value(nargs + 1);
4053-
let callable_tag = callable as *const PyObject as u32;
4054-
if cached_tag == callable_tag {
4019+
if callable.downcast_ref::<PyFunction>().is_some()
4020+
|| callable.downcast_ref::<PyBoundMethod>().is_some()
4021+
{
40554022
let args = self.collect_positional_args(nargs);
40564023
return self.execute_call(args, vm);
40574024
}
@@ -4149,13 +4116,11 @@ impl ExecutingFrame<'_> {
41494116
self.execute_call(args, vm)
41504117
}
41514118
Instruction::CallKwNonPy => {
4152-
let instr_idx = self.lasti() as usize - 1;
4153-
let cache_base = instr_idx + 1;
4154-
let cached_tag = self.code.instructions.read_cache_u32(cache_base + 1);
41554119
let nargs: u32 = arg.into();
41564120
let callable = self.nth_value(nargs + 2);
4157-
let callable_tag = callable as *const PyObject as u32;
4158-
if cached_tag == callable_tag {
4121+
if callable.downcast_ref::<PyFunction>().is_some()
4122+
|| callable.downcast_ref::<PyBoundMethod>().is_some()
4123+
{
41594124
let args = self.collect_keyword_args(nargs);
41604125
return self.execute_call(args, vm);
41614126
}
@@ -6920,7 +6885,6 @@ impl ExecutingFrame<'_> {
69206885

69216886
// Try to specialize method descriptor calls
69226887
if self_or_null_is_some && let Some(descr) = callable.downcast_ref::<PyMethodDescriptor>() {
6923-
let callable_tag = callable as *const PyObject as u32;
69246888
let call_cache_entries = Instruction::CallListAppend.cache_entries();
69256889
let next_idx = cache_base + call_cache_entries;
69266890
let next_is_pop_top = if next_idx < self.code.instructions.len() {
@@ -6943,11 +6907,6 @@ impl ExecutingFrame<'_> {
69436907
_ => Instruction::CallMethodDescriptorFast,
69446908
}
69456909
};
6946-
unsafe {
6947-
self.code
6948-
.instructions
6949-
.write_cache_u32(cache_base + 1, callable_tag);
6950-
}
69516910
self.specialize_at(instr_idx, cache_base, new_op);
69526911
return;
69536912
}
@@ -6966,10 +6925,12 @@ impl ExecutingFrame<'_> {
69666925
};
69676926
let new_op = Some(new_op);
69686927
if let Some(new_op) = new_op {
6969-
unsafe {
6970-
self.code
6971-
.instructions
6972-
.write_cache_u32(cache_base + 1, callable_tag);
6928+
if matches!(new_op, Instruction::CallLen | Instruction::CallIsinstance) {
6929+
unsafe {
6930+
self.code
6931+
.instructions
6932+
.write_cache_u32(cache_base + 1, callable_tag);
6933+
}
69736934
}
69746935
self.specialize_at(instr_idx, cache_base, new_op);
69756936
return;
@@ -7027,24 +6988,12 @@ impl ExecutingFrame<'_> {
70276988
}
70286989
}
70296990
// General builtin class call (any type with Callable)
7030-
let callable_tag = callable as *const PyObject as u32;
7031-
unsafe {
7032-
self.code
7033-
.instructions
7034-
.write_cache_u32(cache_base + 1, callable_tag);
7035-
}
70366991
self.specialize_at(instr_idx, cache_base, Instruction::CallBuiltinClass);
70376992
return;
70386993
}
70396994
}
70406995

7041-
// General fallback: cache callable identity to skip re-specialization
7042-
let callable_tag = callable as *const PyObject as u32;
7043-
unsafe {
7044-
self.code
7045-
.instructions
7046-
.write_cache_u32(cache_base + 1, callable_tag);
7047-
}
6996+
// General fallback: specialized non-Python callable path
70486997
self.specialize_at(instr_idx, cache_base, Instruction::CallNonPyGeneral);
70496998
}
70506999

@@ -7116,13 +7065,7 @@ impl ExecutingFrame<'_> {
71167065
return;
71177066
}
71187067

7119-
// General fallback
7120-
let callable_tag = callable as *const PyObject as u32;
7121-
unsafe {
7122-
self.code
7123-
.instructions
7124-
.write_cache_u32(cache_base + 1, callable_tag);
7125-
}
7068+
// General fallback: specialized non-Python callable path
71267069
self.specialize_at(instr_idx, cache_base, Instruction::CallKwNonPy);
71277070
}
71287071

0 commit comments

Comments
 (0)