@@ -529,6 +529,10 @@ impl PyFunction {
529529}
530530
531531impl Py < PyFunction > {
532+ pub ( crate ) fn is_optimized_for_call_specialization ( & self ) -> bool {
533+ self . code . flags . contains ( bytecode:: CodeFlags :: OPTIMIZED )
534+ }
535+
532536 pub fn invoke_with_locals (
533537 & self ,
534538 func_args : FuncArgs ,
@@ -636,43 +640,90 @@ impl Py<PyFunction> {
636640 new_v
637641 }
638642
643+ /// function_kind(SIMPLE_FUNCTION) equivalent for CALL specialization.
644+ /// Returns true if: CO_OPTIMIZED, no VARARGS, no VARKEYWORDS, no kwonly args.
645+ pub ( crate ) fn is_simple_for_call_specialization ( & self ) -> bool {
646+ let code: & Py < PyCode > = & self . code ;
647+ let flags = code. flags ;
648+ flags. contains ( bytecode:: CodeFlags :: OPTIMIZED )
649+ && !flags. intersects ( bytecode:: CodeFlags :: VARARGS | bytecode:: CodeFlags :: VARKEYWORDS )
650+ && code. kwonlyarg_count == 0
651+ }
652+
639653 /// Check if this function is eligible for exact-args call specialization.
640- /// Returns true if: no VARARGS, no VARKEYWORDS, no kwonly args, not generator/coroutine ,
654+ /// Returns true if: CO_OPTIMIZED, no VARARGS, no VARKEYWORDS, no kwonly args,
641655 /// and effective_nargs matches co_argcount.
642656 pub ( crate ) fn can_specialize_call ( & self , effective_nargs : u32 ) -> bool {
643657 let code: & Py < PyCode > = & self . code ;
644658 let flags = code. flags ;
645- flags. contains ( bytecode:: CodeFlags :: NEWLOCALS )
646- && !flags. intersects (
647- bytecode:: CodeFlags :: VARARGS
648- | bytecode:: CodeFlags :: VARKEYWORDS
649- | bytecode:: CodeFlags :: GENERATOR
650- | bytecode:: CodeFlags :: COROUTINE ,
651- )
659+ flags. contains ( bytecode:: CodeFlags :: OPTIMIZED )
660+ && !flags. intersects ( bytecode:: CodeFlags :: VARARGS | bytecode:: CodeFlags :: VARKEYWORDS )
652661 && code. kwonlyarg_count == 0
653662 && code. arg_count == effective_nargs
654663 }
655664
665+ /// Runtime guard for CALL_*_EXACT_ARGS specialization: check only argcount.
666+ /// Other invariants are guaranteed by function versioning and specialization-time checks.
667+ #[ inline]
668+ pub ( crate ) fn has_exact_argcount ( & self , effective_nargs : u32 ) -> bool {
669+ self . code . arg_count == effective_nargs
670+ }
671+
672+ /// Bytes required for this function's frame on RustPython's thread datastack.
673+ /// Returns `None` for generator/coroutine code paths that do not push a
674+ /// regular datastack-backed frame in the fast call path.
675+ pub ( crate ) fn datastack_frame_size_bytes ( & self ) -> Option < usize > {
676+ let code: & Py < PyCode > = & self . code ;
677+ if code
678+ . flags
679+ . intersects ( bytecode:: CodeFlags :: GENERATOR | bytecode:: CodeFlags :: COROUTINE )
680+ {
681+ return None ;
682+ }
683+ let nlocalsplus = code
684+ . varnames
685+ . len ( )
686+ . checked_add ( code. cellvars . len ( ) ) ?
687+ . checked_add ( code. freevars . len ( ) ) ?;
688+ let capacity = nlocalsplus. checked_add ( code. max_stackdepth as usize ) ?;
689+ capacity. checked_mul ( core:: mem:: size_of :: < usize > ( ) )
690+ }
691+
656692 /// Fast path for calling a simple function with exact positional args.
657693 /// Skips FuncArgs allocation, prepend_arg, and fill_locals_from_args.
658- /// Only valid when: no VARARGS, no VARKEYWORDS, no kwonlyargs, not generator/coroutine ,
694+ /// Only valid when: CO_OPTIMIZED, no VARARGS, no VARKEYWORDS, no kwonlyargs,
659695 /// and nargs == co_argcount.
660696 pub fn invoke_exact_args ( & self , mut args : Vec < PyObjectRef > , vm : & VirtualMachine ) -> PyResult {
661697 let code: PyRef < PyCode > = ( * self . code ) . to_owned ( ) ;
662698
663699 debug_assert_eq ! ( args. len( ) , code. arg_count as usize ) ;
664- debug_assert ! ( code. flags. contains( bytecode:: CodeFlags :: NEWLOCALS ) ) ;
665- debug_assert ! ( !code. flags. intersects(
666- bytecode:: CodeFlags :: VARARGS
667- | bytecode:: CodeFlags :: VARKEYWORDS
668- | bytecode:: CodeFlags :: GENERATOR
669- | bytecode:: CodeFlags :: COROUTINE
670- ) ) ;
700+ debug_assert ! ( code. flags. contains( bytecode:: CodeFlags :: OPTIMIZED ) ) ;
701+ debug_assert ! (
702+ !code
703+ . flags
704+ . intersects( bytecode:: CodeFlags :: VARARGS | bytecode:: CodeFlags :: VARKEYWORDS )
705+ ) ;
671706 debug_assert_eq ! ( code. kwonlyarg_count, 0 ) ;
672707
708+ // Generator/coroutine code objects are SIMPLE_FUNCTION in call
709+ // specialization classification, but their call path must still
710+ // go through invoke() to produce generator/coroutine objects.
711+ if code
712+ . flags
713+ . intersects ( bytecode:: CodeFlags :: GENERATOR | bytecode:: CodeFlags :: COROUTINE )
714+ {
715+ return self . invoke ( FuncArgs :: from ( args) , vm) ;
716+ }
717+
718+ let locals = if code. flags . contains ( bytecode:: CodeFlags :: NEWLOCALS ) {
719+ None
720+ } else {
721+ Some ( ArgMapping :: from_dict_exact ( self . globals . clone ( ) ) )
722+ } ;
723+
673724 let frame = Frame :: new (
674725 code. clone ( ) ,
675- Scope :: new ( None , self . globals . clone ( ) ) ,
726+ Scope :: new ( locals , self . globals . clone ( ) ) ,
676727 self . builtins . clone ( ) ,
677728 self . closure . as_ref ( ) . map_or ( & [ ] , |c| c. as_slice ( ) ) ,
678729 Some ( self . to_owned ( ) . into ( ) ) ,
0 commit comments