Skip to content

Commit 5ac7f95

Browse files
committed
Add CallKwPy, CallKwBoundMethod, CallKwNonPy, BinaryOpSubscrListSlice specialization
1 parent 89d0695 commit 5ac7f95

File tree

1 file changed

+152
-1
lines changed

1 file changed

+152
-1
lines changed

crates/vm/src/frame.rs

Lines changed: 152 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1293,8 +1293,21 @@ impl ExecutingFrame<'_> {
12931293
self.execute_call(args, vm)
12941294
}
12951295
Instruction::CallKw { nargs } => {
1296+
let nargs = nargs.get(arg);
1297+
let instr_idx = self.lasti() as usize - 1;
1298+
let cache_base = instr_idx + 1;
1299+
let counter = self.code.instructions.read_cache_u16(cache_base);
1300+
if counter > 0 {
1301+
unsafe {
1302+
self.code
1303+
.instructions
1304+
.write_cache_u16(cache_base, counter - 1);
1305+
}
1306+
} else {
1307+
self.specialize_call_kw(vm, nargs, instr_idx, cache_base);
1308+
}
12961309
// Stack: [callable, self_or_null, arg1, ..., argN, kwarg_names]
1297-
let args = self.collect_keyword_args(nargs.get(arg));
1310+
let args = self.collect_keyword_args(nargs);
12981311
self.execute_call(args, vm)
12991312
}
13001313
Instruction::CallFunctionEx => {
@@ -4086,6 +4099,77 @@ impl ExecutingFrame<'_> {
40864099
let args = self.collect_positional_args(nargs);
40874100
self.execute_call(args, vm)
40884101
}
4102+
Instruction::CallKwPy => {
4103+
let instr_idx = self.lasti() as usize - 1;
4104+
let cache_base = instr_idx + 1;
4105+
let cached_version = self.code.instructions.read_cache_u32(cache_base + 1);
4106+
let nargs: u32 = arg.into();
4107+
// Stack: [callable, self_or_null, arg1, ..., argN, kwarg_names]
4108+
// callable is at position nargs + 2 from top (nargs args + kwarg_names + self_or_null)
4109+
let callable = self.nth_value(nargs + 2);
4110+
if let Some(func) = callable.downcast_ref::<PyFunction>()
4111+
&& func.func_version() == cached_version
4112+
&& cached_version != 0
4113+
{
4114+
let args = self.collect_keyword_args(nargs);
4115+
let self_or_null = self.pop_value_opt();
4116+
let callable = self.pop_value();
4117+
let func = callable.downcast_ref::<PyFunction>().unwrap();
4118+
let final_args = if let Some(self_val) = self_or_null {
4119+
let mut args = args;
4120+
args.prepend_arg(self_val);
4121+
args
4122+
} else {
4123+
args
4124+
};
4125+
let result = func.invoke(final_args, vm)?;
4126+
self.push_value(result);
4127+
return Ok(None);
4128+
}
4129+
self.deoptimize_call_kw();
4130+
let args = self.collect_keyword_args(nargs);
4131+
self.execute_call(args, vm)
4132+
}
4133+
Instruction::CallKwBoundMethod => {
4134+
let instr_idx = self.lasti() as usize - 1;
4135+
let cache_base = instr_idx + 1;
4136+
let cached_version = self.code.instructions.read_cache_u32(cache_base + 1);
4137+
let nargs: u32 = arg.into();
4138+
// Stack: [callable, self_or_null(=self), arg1, ..., argN, kwarg_names]
4139+
let callable = self.nth_value(nargs + 2);
4140+
if let Some(func) = callable.downcast_ref::<PyFunction>()
4141+
&& func.func_version() == cached_version
4142+
&& cached_version != 0
4143+
{
4144+
let args = self.collect_keyword_args(nargs);
4145+
let self_val = self.pop_value(); // self_or_null is always Some here
4146+
let callable = self.pop_value();
4147+
let func = callable.downcast_ref::<PyFunction>().unwrap();
4148+
let mut final_args = args;
4149+
final_args.prepend_arg(self_val);
4150+
let result = func.invoke(final_args, vm)?;
4151+
self.push_value(result);
4152+
return Ok(None);
4153+
}
4154+
self.deoptimize_call_kw();
4155+
let args = self.collect_keyword_args(nargs);
4156+
self.execute_call(args, vm)
4157+
}
4158+
Instruction::CallKwNonPy => {
4159+
let instr_idx = self.lasti() as usize - 1;
4160+
let cache_base = instr_idx + 1;
4161+
let cached_tag = self.code.instructions.read_cache_u32(cache_base + 1);
4162+
let nargs: u32 = arg.into();
4163+
let callable = self.nth_value(nargs + 2);
4164+
let callable_tag = callable as *const PyObject as u32;
4165+
if cached_tag == callable_tag {
4166+
let args = self.collect_keyword_args(nargs);
4167+
return self.execute_call(args, vm);
4168+
}
4169+
self.deoptimize_call_kw();
4170+
let args = self.collect_keyword_args(nargs);
4171+
self.execute_call(args, vm)
4172+
}
40894173
Instruction::LoadSuperAttrAttr => {
40904174
let oparg = u32::from(arg);
40914175
let attr_name = self.code.names[(oparg >> 2) as usize];
@@ -6681,6 +6765,57 @@ impl ExecutingFrame<'_> {
66816765
}
66826766
}
66836767

6768+
fn specialize_call_kw(
6769+
&mut self,
6770+
_vm: &VirtualMachine,
6771+
nargs: u32,
6772+
instr_idx: usize,
6773+
cache_base: usize,
6774+
) {
6775+
// Stack: [callable, self_or_null, arg1, ..., argN, kwarg_names]
6776+
// callable is at position nargs + 2 from top
6777+
let stack = &self.state.stack;
6778+
let stack_len = stack.len();
6779+
let self_or_null_is_some = stack[stack_len - nargs as usize - 2].is_some();
6780+
let callable = self.nth_value(nargs + 2);
6781+
6782+
if let Some(func) = callable.downcast_ref::<PyFunction>() {
6783+
let version = func.func_version();
6784+
if version == 0 {
6785+
unsafe {
6786+
self.code
6787+
.instructions
6788+
.write_adaptive_counter(cache_base, ADAPTIVE_BACKOFF_VALUE);
6789+
}
6790+
return;
6791+
}
6792+
6793+
let new_op = if self_or_null_is_some {
6794+
Instruction::CallKwBoundMethod
6795+
} else {
6796+
Instruction::CallKwPy
6797+
};
6798+
unsafe {
6799+
self.code.instructions.replace_op(instr_idx, new_op);
6800+
self.code
6801+
.instructions
6802+
.write_cache_u32(cache_base + 1, version);
6803+
}
6804+
return;
6805+
}
6806+
6807+
// General fallback
6808+
let callable_tag = callable as *const PyObject as u32;
6809+
unsafe {
6810+
self.code
6811+
.instructions
6812+
.replace_op(instr_idx, Instruction::CallKwNonPy);
6813+
self.code
6814+
.instructions
6815+
.write_cache_u32(cache_base + 1, callable_tag);
6816+
}
6817+
}
6818+
66846819
fn specialize_send(&mut self, instr_idx: usize, cache_base: usize) {
66856820
// Stack: [receiver, val] — receiver is at position 1
66866821
let receiver = self.nth_value(1);
@@ -6885,6 +7020,22 @@ impl ExecutingFrame<'_> {
68857020
}
68867021
}
68877022

7023+
fn deoptimize_call_kw(&mut self) {
7024+
let instr_idx = self.lasti() as usize - 1;
7025+
let cache_base = instr_idx + 1;
7026+
unsafe {
7027+
self.code.instructions.replace_op(
7028+
instr_idx,
7029+
Instruction::CallKw {
7030+
nargs: Arg::marker(),
7031+
},
7032+
);
7033+
self.code
7034+
.instructions
7035+
.write_adaptive_counter(cache_base, ADAPTIVE_BACKOFF_VALUE);
7036+
}
7037+
}
7038+
68887039
fn deoptimize_for_iter(&mut self) {
68897040
let instr_idx = self.lasti() as usize - 1;
68907041
let cache_base = instr_idx + 1;

0 commit comments

Comments
 (0)