@@ -2502,196 +2502,29 @@ pub fn compile_call_expr_with_returns_and_dest(
25022502 }
25032503
25042504 // OPTIMIZATION: If last argument is a call, use "all out" mode
2505- // Use recursive compile_call_expr_with_returns to support method calls (SELF instruction)
2505+ // Simply recursively compile the call with usize::MAX returns - do NOT manually handle inner args
25062506 if is_last && matches ! ( arg_expr, LuaExpr :: CallExpr ( _) ) {
2507- if let LuaExpr :: CallExpr ( call_expr) = arg_expr {
2508- // Compile inner call with "all out" mode (num_returns = 0 means variable returns)
2509- // Note: We need to handle this specially for method calls
2510- let inner_prefix = call_expr. get_prefix_expr ( ) . ok_or ( "missing call prefix" ) ?;
2511-
2512- // Check if inner call is a method call
2513- let inner_is_method = if let LuaExpr :: IndexExpr ( index_expr) = & inner_prefix {
2514- index_expr
2515- . get_index_token ( )
2516- . map ( |t| t. is_colon ( ) )
2517- . unwrap_or ( false )
2518- } else {
2519- false
2520- } ;
2521-
2522- // Handle method call with SELF instruction
2523- let call_reg = if inner_is_method {
2524- if let LuaExpr :: IndexExpr ( index_expr) = & inner_prefix {
2525- // CRITICAL FIX: For method call as last argument, we need to place
2526- // the function at arg_dest so the call result ends up at the correct position.
2527- // SELF A B C: R[A+1] = R[B]; R[A] = R[B][C]
2528- // So we want A = arg_dest, and need to reserve arg_dest+1 for self
2529- let func_reg = arg_dest;
2530- ensure_register ( c, func_reg) ;
2531- ensure_register ( c, func_reg + 1 ) ; // Reserve for self
2532-
2533- let obj_expr = index_expr
2534- . get_prefix_expr ( )
2535- . ok_or ( "Method call missing object" ) ?;
2536- let obj_reg = compile_expr ( c, & obj_expr) ?;
2537-
2538- let method_name = if let Some ( LuaIndexKey :: Name ( name_token) ) =
2539- index_expr. get_index_key ( )
2540- {
2541- name_token. get_name_text ( ) . to_string ( )
2542- } else {
2543- return Err ( "Method call requires name index" . to_string ( ) ) ;
2544- } ;
2545-
2546- let lua_str = create_string_value ( c, & method_name) ;
2547- let key_idx = add_constant_dedup ( c, lua_str) ;
2548-
2549- emit (
2550- c,
2551- Instruction :: create_abck (
2552- OpCode :: Self_ ,
2553- func_reg,
2554- obj_reg,
2555- key_idx,
2556- true ,
2557- ) ,
2558- ) ;
2559-
2560- func_reg
2561- } else {
2562- unreachable ! ( "inner_is_method but not IndexExpr" )
2563- }
2564- } else {
2565- // For regular call, we need to place it at arg_dest (outer call's argument position)
2566- // to avoid register conflicts
2567- let temp_func_reg = compile_expr ( c, & inner_prefix) ?;
2568- if temp_func_reg != arg_dest {
2569- ensure_register ( c, arg_dest) ;
2570- emit_move ( c, arg_dest, temp_func_reg) ;
2571- arg_dest
2572- } else {
2573- temp_func_reg
2574- }
2575- } ;
2576-
2577- // Compile call arguments
2578- let call_args_start = if inner_is_method {
2579- call_reg + 2
2580- } else {
2581- call_reg + 1
2582- } ;
2583- let call_arg_exprs = call_expr
2584- . get_args_list ( )
2585- . ok_or ( "missing args list" ) ?
2586- . get_args ( )
2587- . collect :: < Vec < _ > > ( ) ;
2588-
2589- // CRITICAL: Reset freereg so inner call's arguments compile into correct positions
2590- c. freereg = call_args_start;
2591-
2592- let mut call_arg_regs = Vec :: new ( ) ;
2593- // CRITICAL: Allocate all argument registers upfront to prevent conflicts
2594- let num_call_args = call_arg_exprs. len ( ) ;
2595- let call_args_end = call_args_start + num_call_args as u32 ;
2596- while c. freereg < call_args_end {
2597- alloc_register ( c) ;
2507+ if let LuaExpr :: CallExpr ( inner_call) = arg_expr {
2508+ // Use a simple approach: compile inner call to arg_dest with "all out" mode
2509+ // The recursive call will handle everything including method calls and nested calls
2510+ let call_result = compile_call_expr_with_returns_and_dest ( c, inner_call, usize:: MAX , Some ( arg_dest) ) ?;
2511+ if call_result != arg_dest {
2512+ ensure_register ( c, arg_dest) ;
2513+ emit_move ( c, arg_dest, call_result) ;
25982514 }
2599-
2600- let mut inner_last_arg_is_vararg_or_call = false ;
2601- for ( j, call_arg) in call_arg_exprs. iter ( ) . enumerate ( ) {
2602- let call_arg_dest = call_args_start + j as u32 ;
2603- let is_last_inner_arg = j == call_arg_exprs. len ( ) - 1 ;
2604-
2605- // Reset freereg before each argument to protect argument slots
2606- if c. freereg < call_args_end {
2607- c. freereg = call_args_end;
2608- }
2609-
2610- // Check if last inner argument is vararg
2611- if is_last_inner_arg {
2612- if let LuaExpr :: LiteralExpr ( lit_expr) = call_arg {
2613- if matches ! ( lit_expr. get_literal( ) , Some ( LuaLiteralToken :: Dots ( _) ) ) {
2614- // Vararg as last argument: VARARG with C=0 (all out)
2615- emit ( c, Instruction :: encode_abc ( OpCode :: Vararg , call_arg_dest, 0 , 0 ) ) ;
2616- call_arg_regs. push ( call_arg_dest) ;
2617- inner_last_arg_is_vararg_or_call = true ;
2618- continue ;
2619- }
2620- }
2621- // Check if last arg is a call expression - compile it with "all out"
2622- if let LuaExpr :: CallExpr ( inner_call_expr) = call_arg {
2623- // Recursively compile this call with returns=usize::MAX (all out)
2624- let inner_call_reg = compile_call_expr_with_returns_and_dest ( c, inner_call_expr, usize:: MAX , Some ( call_arg_dest) ) ?;
2625- if inner_call_reg != call_arg_dest {
2626- while c. freereg <= call_arg_dest {
2627- alloc_register ( c) ;
2628- }
2629- emit_move ( c, call_arg_dest, inner_call_reg) ;
2630- }
2631- call_arg_regs. push ( call_arg_dest) ;
2632- inner_last_arg_is_vararg_or_call = true ;
2633- continue ;
2634- }
2635- }
2636-
2637- let arg_reg = compile_expr_to ( c, call_arg, Some ( call_arg_dest) ) ?;
2638- call_arg_regs. push ( arg_reg) ;
2639- }
2640-
2641- // Move call arguments if needed (skip if we emitted VARARG directly)
2642- if !inner_last_arg_is_vararg_or_call {
2643- for ( j, & reg) in call_arg_regs. iter ( ) . enumerate ( ) {
2644- let target = call_args_start + j as u32 ;
2645- if reg != target {
2646- while c. freereg <= target {
2647- alloc_register ( c) ;
2648- }
2649- emit_move ( c, target, reg) ;
2650- }
2651- }
2652- }
2653-
2654- // Emit call with "all out" (C=0)
2655- // B = number of arguments + 1, or 0 if last arg was vararg/call
2656- let inner_b_param = if inner_last_arg_is_vararg_or_call {
2657- 0 // B=0: variable number of args
2658- } else if inner_is_method {
2659- ( num_call_args + 2 ) as u32 // +1 for self, +1 for Lua convention
2660- } else {
2661- ( num_call_args + 1 ) as u32
2662- } ;
2663- emit (
2664- c,
2665- Instruction :: encode_abc (
2666- OpCode :: Call ,
2667- call_reg,
2668- inner_b_param,
2669- 0 , // C=0: all out
2670- ) ,
2671- ) ;
2672-
2673- arg_regs. push ( call_reg) ;
2515+ arg_regs. push ( arg_dest) ;
26742516 last_arg_is_call_all_out = true ;
26752517 break ;
26762518 }
26772519 }
26782520
26792521 // Compile argument directly to its target position
2680- let arg_reg = if matches ! ( arg_expr, LuaExpr :: CallExpr ( _) ) {
2681- // Pass dest to allow nested expressions to use correct registers
2682- let call_reg = compile_expr_to ( c, arg_expr, Some ( arg_dest) ) ?;
2683- // Move result to target position if needed
2684- if call_reg != arg_dest {
2685- ensure_register ( c, arg_dest) ;
2686- emit_move ( c, arg_dest, call_reg) ;
2687- arg_dest
2688- } else {
2689- call_reg
2690- }
2691- } else {
2692- compile_expr_to ( c, arg_expr, Some ( arg_dest) ) ?
2693- } ;
2694- arg_regs. push ( arg_reg) ;
2522+ let arg_reg = compile_expr_to ( c, arg_expr, Some ( arg_dest) ) ?;
2523+ if arg_reg != arg_dest {
2524+ ensure_register ( c, arg_dest) ;
2525+ emit_move ( c, arg_dest, arg_reg) ;
2526+ }
2527+ arg_regs. push ( arg_dest) ;
26952528 }
26962529
26972530 // Restore freereg to saved value or update to after last argument
@@ -2759,21 +2592,28 @@ pub fn compile_call_expr_with_returns_and_dest(
27592592 // CALL places return values starting at func_reg
27602593 // If num_returns == 0, CALL discards all returns
27612594 // If num_returns > 0, return values are in func_reg .. func_reg + num_returns - 1
2595+ // If num_returns == usize::MAX, it's "all out" mode - we don't know how many returns
27622596 //
27632597 // CRITICAL: freereg can only be set to func_reg + num_returns if that's >= nactvar
27642598 // We cannot reclaim registers occupied by active local variables!
2765- let new_freereg = func_reg + num_returns as u32 ;
2766- if new_freereg >= c. nactvar as u32 {
2767- c. freereg = new_freereg;
2599+ if num_returns != usize:: MAX {
2600+ let new_freereg = func_reg + num_returns as u32 ;
2601+ if new_freereg >= c. nactvar as u32 {
2602+ c. freereg = new_freereg;
2603+ }
27682604 }
2769- // If new_freereg < nactvar, keep freereg unchanged (locals are still alive)
2605+ // For "all out" mode (num_returns == usize::MAX), keep freereg unchanged
2606+ // The caller (table constructor, etc.) will handle the stack properly
27702607
27712608 // If we had to move function to avoid conflicts, move return values back to original dest
27722609 if need_move_to_dest {
27732610 if let Some ( d) = original_dest {
27742611 // Move return values from func_reg to original dest
2775- for i in 0 ..num_returns {
2776- emit_move ( c, d + i as u32 , func_reg + i as u32 ) ;
2612+ // CRITICAL: Don't do this for "all out" mode - we don't know how many values
2613+ if num_returns != usize:: MAX {
2614+ for i in 0 ..num_returns {
2615+ emit_move ( c, d + i as u32 , func_reg + i as u32 ) ;
2616+ }
27772617 }
27782618 return Ok ( d) ;
27792619 }
@@ -3259,7 +3099,6 @@ fn compile_table_expr_to(
32593099 if let Some ( idx) = call_at_end_idx {
32603100 // Call as last element: compile call with all return values
32613101 let target_reg = values_start + array_idx;
3262- eprintln ! ( "[DEBUG] call_at_end: target_reg={}, freereg={}, values_start={}, array_idx={}" , target_reg, c. freereg, values_start, array_idx) ;
32633102 while c. freereg <= target_reg {
32643103 alloc_register ( c) ;
32653104 }
@@ -3269,9 +3108,7 @@ fn compile_table_expr_to(
32693108 if let Some ( value_expr) = field. get_value_expr ( ) {
32703109 if let LuaExpr :: CallExpr ( call_expr) = value_expr {
32713110 // Compile the call with all return values (usize::MAX means all)
3272- eprintln ! ( "[DEBUG] before compile_call_expr" ) ;
32733111 compile_call_expr_with_returns_and_dest ( c, & call_expr, usize:: MAX , Some ( target_reg) ) ?;
3274- eprintln ! ( "[DEBUG] after compile_call_expr" ) ;
32753112 }
32763113 }
32773114 }
0 commit comments