@@ -2679,12 +2679,15 @@ impl RegVm {
26792679 }
26802680 }
26812681
2682- /// Native mutual recursion (native-call-ABI slice 4): if `function_id` is part of
2683- /// a mutually-recursive scalar-`Int` cycle, compile the whole group together
2684- /// (declare the cycle, then define each) and dispatch the called member natively.
2685- /// Returns the result on a clean completion, or `None` to fall back to the
2686- /// interpreter (not eligible, or a deopt incl. the entry depth-cap bail). The
2687- /// group is compiled once and every member cached.
2682+ /// Native mutual recursion (native-call-ABI slice 4; generalized to scalar Float):
2683+ /// if `function_id` is part of a mutually-recursive cycle of scalar functions,
2684+ /// compile the whole group together (declare the cycle, then define each) and
2685+ /// dispatch the called member natively. Arg/return marshalling follows each
2686+ /// member's compiled scalar parameter/return `NativeTy`s (Int/Bool/Float — Float
2687+ /// via `to_bits`/`from_bits`), exactly like `try_native_self_recursive`. Returns
2688+ /// the result on a clean completion, or `None` to fall back to the interpreter
2689+ /// (not eligible, non-scalar param/return, or a deopt incl. the depth-cap bail).
2690+ /// The group is compiled once and every member cached.
26882691 #[ cfg( feature = "native-jit" ) ]
26892692 pub ( super ) fn try_native_mutual_recursive_int (
26902693 & mut self ,
@@ -2698,10 +2701,10 @@ impl RegVm {
26982701 return None ;
26992702 }
27002703 let key = Rc :: as_ptr ( func) as usize ;
2701- // Resolve the called member's native id and its return kind (Int → wrap the
2702- // result `i64` as `VmValue::Int`, Bool → as `VmValue::Bool` ). Cached per
2703- // member; compiling any member compiles the whole group.
2704- let ( id, returns_bool ) = {
2704+ // Resolve the called member's native id, its parameter types (to marshal each
2705+ // scalar arg) and its return type (to wrap the i64 result ). Cached per member;
2706+ // compiling any member compiles the whole group.
2707+ let ( id, param_tys , ret ) = {
27052708 let native = self . native . as_mut ( ) ?;
27062709 if native. force_bail
27072710 || native. forced_safepoint . is_some ( )
@@ -2710,17 +2713,19 @@ impl RegVm {
27102713 return None ;
27112714 }
27122715 if let Some ( cached) = native. mutual_recursive_native . get ( & key) {
2713- ( * cached) ?
2716+ cached. clone ( ) ?
27142717 } else {
2715- let group = native_recursive_int_group ( unit, function_id) ;
2718+ let group = native_recursive_group ( unit, function_id) ;
2719+ let is_scalar =
2720+ |t : & NativeTy | matches ! ( t, NativeTy :: Int | NativeTy :: Bool | NativeTy :: Float ) ;
27162721 let compiled = group. as_ref ( ) . and_then ( |scc| {
27172722 let index_of: std:: collections:: HashMap < usize , u32 > = scc
27182723 . iter ( )
27192724 . enumerate ( )
27202725 . map ( |( i, & fid) | ( fid, i as u32 ) )
27212726 . collect ( ) ;
27222727 let mut jit_funcs = Vec :: with_capacity ( scc. len ( ) ) ;
2723- let mut member_returns_bool = Vec :: with_capacity ( scc. len ( ) ) ;
2728+ let mut member_sigs = Vec :: with_capacity ( scc. len ( ) ) ;
27242729 for & member in scc {
27252730 let mfunc = unit. functions . get ( member) ?;
27262731 let group_call_sites: std:: collections:: HashMap < usize , u32 > = mfunc
@@ -2736,32 +2741,34 @@ impl RegVm {
27362741 _ => None ,
27372742 } )
27382743 . collect ( ) ;
2739- let ( jit_fn, ret, _p , _l, _pr) = translate_to_native_jit_with_calls (
2744+ let ( jit_fn, ret, param_tys , _l, _pr) = translate_to_native_jit_with_calls (
27402745 unit,
27412746 mfunc,
27422747 & std:: collections:: HashMap :: new ( ) ,
27432748 & std:: collections:: HashSet :: new ( ) ,
27442749 & group_call_sites,
27452750 ) ?;
2746- // i64-scalar returns only: Int or Bool (both wrap from i64).
2747- let returns_bool = match ret {
2748- NativeTy :: Int => false ,
2749- NativeTy :: Bool => true ,
2750- _ => return None ,
2751- } ;
2752- member_returns_bool. push ( returns_bool) ;
2751+ // Scalar-only ABI: params and return must be Int/Bool/Float
2752+ // (each wraps to/from an i64 slot). Heap params/returns decline.
2753+ if !is_scalar ( & ret) || !param_tys. iter ( ) . all ( is_scalar) {
2754+ return None ;
2755+ }
2756+ member_sigs. push ( ( param_tys, ret) ) ;
27532757 jit_funcs. push ( jit_fn) ;
27542758 }
27552759 let ids = native. module . compile_recursive_group ( & jit_funcs) . ok ( ) ?;
2756- Some ( ( ids, member_returns_bool ) )
2760+ Some ( ( ids, member_sigs ) )
27572761 } ) ;
27582762 match ( group, compiled) {
2759- ( Some ( scc) , Some ( ( ids, member_returns_bool ) ) ) if ids. len ( ) == scc. len ( ) => {
2763+ ( Some ( scc) , Some ( ( ids, member_sigs ) ) ) if ids. len ( ) == scc. len ( ) => {
27602764 let mut mine = None ;
27612765 for ( i, & member) in scc. iter ( ) . enumerate ( ) {
27622766 let mkey = Rc :: as_ptr ( & unit. functions [ member] ) as usize ;
2763- let entry = ( ids[ i] , member_returns_bool[ i] ) ;
2764- native. mutual_recursive_native . insert ( mkey, Some ( entry) ) ;
2767+ let ( param_tys, ret) = member_sigs[ i] . clone ( ) ;
2768+ let entry = ( ids[ i] , param_tys, ret) ;
2769+ native
2770+ . mutual_recursive_native
2771+ . insert ( mkey, Some ( entry. clone ( ) ) ) ;
27652772 if member == function_id {
27662773 mine = Some ( entry) ;
27672774 }
@@ -2786,12 +2793,20 @@ impl RegVm {
27862793 }
27872794 }
27882795 } ;
2796+ if param_tys. len ( ) != args. len ( ) {
2797+ return None ;
2798+ }
27892799 let mut int_args = Vec :: with_capacity ( args. len ( ) ) ;
2790- for & arg in args {
2791- // Scalar value args marshal to `i64` (Bool as 0/1, like the native ABI).
2792- let bits = match self . reg ( caller_base + arg) {
2793- VmValue :: Int ( value) => * value,
2794- VmValue :: Bool ( value) => * value as i64 ,
2800+ for ( & arg, pty) in args. iter ( ) . zip ( param_tys. iter ( ) ) {
2801+ // Scalar value args marshal to an i64 slot, driven by the compiled
2802+ // parameter type (Float reinterpreted via `to_bits`; an Int value for a
2803+ // Float param converted first) — identical to the self-recursion path.
2804+ let bits = match ( pty, self . reg ( caller_base + arg) ) {
2805+ ( NativeTy :: Float , VmValue :: Float ( f) ) => f. to_bits ( ) as i64 ,
2806+ ( NativeTy :: Float , VmValue :: Int ( i) ) => ( * i as f64 ) . to_bits ( ) as i64 ,
2807+ ( _, VmValue :: Int ( i) ) => * i,
2808+ ( _, VmValue :: Bool ( b) ) => i64:: from ( * b) ,
2809+ ( _, VmValue :: Float ( f) ) => f. to_bits ( ) as i64 ,
27952810 _ => return None ,
27962811 } ;
27972812 int_args. push ( bits) ;
@@ -2812,10 +2827,10 @@ impl RegVm {
28122827 {
28132828 native. stats . native_calls += 1 ;
28142829 }
2815- Some ( if returns_bool {
2816- VmValue :: Bool ( bits != 0 )
2817- } else {
2818- VmValue :: Int ( bits)
2830+ Some ( match ret {
2831+ NativeTy :: Float => VmValue :: Float ( f64 :: from_bits ( bits as u64 ) ) ,
2832+ NativeTy :: Bool => VmValue :: Bool ( bits != 0 ) ,
2833+ _ => VmValue :: Int ( bits) ,
28192834 } )
28202835 }
28212836 _ => {
@@ -3304,11 +3319,14 @@ fn compute_recursive_int_member_inner(
33043319}
33053320
33063321/// The mutually-recursive group (call-graph SCC) containing `function_id`, if it is
3307- /// a cycle of >= 2 all-scalar-`Int` functions whose every `CallKnown` targets a
3308- /// group member (native-call-ABI slice 4). Returned sorted; `None` for non-cyclic
3309- /// functions and pure self-recursion (handled by the self-recursive path).
3322+ /// a cycle of >= 2 functions (native-call-ABI slice 4). Returned sorted; `None` for
3323+ /// non-cyclic functions and pure self-recursion (handled by the self-recursive
3324+ /// path). Per-member native eligibility (scalar params/return, native-subset body)
3325+ /// is NOT decided here — the caller's `translate_to_native_jit_with_calls` per member
3326+ /// is the single eligibility gate, so the group admits any cycle the general native
3327+ /// path can compile (Int/Bool/Float bodies, members that also call inlinable leaves).
33103328#[ cfg( feature = "native-jit" ) ]
3311- fn native_recursive_int_group ( unit : & RegUnit , function_id : usize ) -> Option < Vec < usize > > {
3329+ fn native_recursive_group ( unit : & RegUnit , function_id : usize ) -> Option < Vec < usize > > {
33123330 use std:: collections:: HashSet ;
33133331 let callees = |fid : usize | -> Vec < usize > {
33143332 unit. functions . get ( fid) . map_or_else ( Vec :: new, |f| {
@@ -3347,15 +3365,6 @@ fn native_recursive_int_group(unit: &RegUnit, function_id: usize) -> Option<Vec<
33473365 if scc. len ( ) < 2 {
33483366 return None ;
33493367 }
3350- let group: HashSet < usize > = scc. iter ( ) . copied ( ) . collect ( ) ;
3351- for & member in & scc {
3352- // Mutual recursion falls back to the full interpreter (not the i64 tier-0
3353- // executor), so any scalar value return kind (Int or Bool) is admissible.
3354- match compute_recursive_int_member_inner ( unit, member, & group) {
3355- Some ( ScalarSlotKind :: Int | ScalarSlotKind :: Bool ) => { }
3356- _ => return None ,
3357- }
3358- }
33593368 Some ( scc)
33603369}
33613370
0 commit comments