@@ -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