@@ -403,41 +403,79 @@ fn compile_binary_expr_desc(c: &mut Compiler, expr: &LuaBinaryExpr) -> Result<Ex
403403 } else {
404404 alloc_register ( c)
405405 } ;
406- emit (
407- c,
408- Instruction :: encode_abc ( OpCode :: Add , result_reg, left_reg, right_reg) ,
409- ) ;
410- emit (
411- c,
412- Instruction :: create_abck (
413- OpCode :: MmBin ,
414- left_reg,
415- right_reg,
416- TagMethod :: Add . as_u32 ( ) ,
417- false ,
418- ) ,
419- ) ;
406+ if can_use_rk {
407+ // Right is constant, use ADDK
408+ let const_idx = ensure_constant ( c, & saved_right_desc) ?;
409+ emit (
410+ c,
411+ Instruction :: encode_abc ( OpCode :: AddK , result_reg, left_reg, const_idx) ,
412+ ) ;
413+ emit (
414+ c,
415+ Instruction :: create_abck (
416+ OpCode :: MmBinK ,
417+ left_reg,
418+ const_idx,
419+ TagMethod :: Add . as_u32 ( ) ,
420+ false ,
421+ ) ,
422+ ) ;
423+ } else {
424+ emit (
425+ c,
426+ Instruction :: encode_abc ( OpCode :: Add , result_reg, left_reg, right_reg) ,
427+ ) ;
428+ emit (
429+ c,
430+ Instruction :: create_abck (
431+ OpCode :: MmBin ,
432+ left_reg,
433+ right_reg,
434+ TagMethod :: Add . as_u32 ( ) ,
435+ false ,
436+ ) ,
437+ ) ;
438+ }
420439 }
421440 BinaryOperator :: OpSub => {
422441 result_reg = if can_reuse_left {
423442 left_reg
424443 } else {
425444 alloc_register ( c)
426445 } ;
427- emit (
428- c,
429- Instruction :: encode_abc ( OpCode :: Sub , result_reg, left_reg, right_reg) ,
430- ) ;
431- emit (
432- c,
433- Instruction :: create_abck (
434- OpCode :: MmBin ,
435- left_reg,
436- right_reg,
437- TagMethod :: Sub . as_u32 ( ) ,
438- false ,
439- ) ,
440- ) ;
446+ if can_use_rk {
447+ // Right is constant, use SUBK
448+ let const_idx = ensure_constant ( c, & saved_right_desc) ?;
449+ emit (
450+ c,
451+ Instruction :: encode_abc ( OpCode :: SubK , result_reg, left_reg, const_idx) ,
452+ ) ;
453+ emit (
454+ c,
455+ Instruction :: create_abck (
456+ OpCode :: MmBinK ,
457+ left_reg,
458+ const_idx,
459+ TagMethod :: Sub . as_u32 ( ) ,
460+ false ,
461+ ) ,
462+ ) ;
463+ } else {
464+ emit (
465+ c,
466+ Instruction :: encode_abc ( OpCode :: Sub , result_reg, left_reg, right_reg) ,
467+ ) ;
468+ emit (
469+ c,
470+ Instruction :: create_abck (
471+ OpCode :: MmBin ,
472+ left_reg,
473+ right_reg,
474+ TagMethod :: Sub . as_u32 ( ) ,
475+ false ,
476+ ) ,
477+ ) ;
478+ }
441479 }
442480 BinaryOperator :: OpMul => {
443481 result_reg = if can_reuse_left {
@@ -2642,33 +2680,68 @@ pub fn compile_call_expr_with_returns_and_dest(
26422680 alloc_register ( c) ;
26432681 }
26442682
2683+ let mut inner_last_arg_is_vararg_or_call = false ;
26452684 for ( j, call_arg) in call_arg_exprs. iter ( ) . enumerate ( ) {
26462685 let call_arg_dest = call_args_start + j as u32 ;
2686+ let is_last_inner_arg = j == call_arg_exprs. len ( ) - 1 ;
2687+
26472688 // Reset freereg before each argument to protect argument slots
26482689 if c. freereg < call_args_end {
26492690 c. freereg = call_args_end;
26502691 }
2692+
2693+ // Check if last inner argument is vararg
2694+ if is_last_inner_arg {
2695+ if let LuaExpr :: LiteralExpr ( lit_expr) = call_arg {
2696+ if matches ! ( lit_expr. get_literal( ) , Some ( LuaLiteralToken :: Dots ( _) ) ) {
2697+ // Vararg as last argument: VARARG with C=0 (all out)
2698+ emit ( c, Instruction :: encode_abc ( OpCode :: Vararg , call_arg_dest, 0 , 0 ) ) ;
2699+ call_arg_regs. push ( call_arg_dest) ;
2700+ inner_last_arg_is_vararg_or_call = true ;
2701+ continue ;
2702+ }
2703+ }
2704+ // Check if last arg is a call expression - compile it with "all out"
2705+ if let LuaExpr :: CallExpr ( inner_call_expr) = call_arg {
2706+ // Recursively compile this call with returns=usize::MAX (all out)
2707+ let inner_call_reg = compile_call_expr_with_returns_and_dest ( c, inner_call_expr, usize:: MAX , Some ( call_arg_dest) ) ?;
2708+ if inner_call_reg != call_arg_dest {
2709+ while c. freereg <= call_arg_dest {
2710+ alloc_register ( c) ;
2711+ }
2712+ emit_move ( c, call_arg_dest, inner_call_reg) ;
2713+ }
2714+ call_arg_regs. push ( call_arg_dest) ;
2715+ inner_last_arg_is_vararg_or_call = true ;
2716+ continue ;
2717+ }
2718+ }
2719+
26512720 let arg_reg = compile_expr_to ( c, call_arg, Some ( call_arg_dest) ) ?;
26522721 call_arg_regs. push ( arg_reg) ;
26532722 }
26542723
2655- // Move call arguments if needed
2656- for ( j, & reg) in call_arg_regs. iter ( ) . enumerate ( ) {
2657- let target = call_args_start + j as u32 ;
2658- if reg != target {
2659- while c. freereg <= target {
2660- alloc_register ( c) ;
2724+ // Move call arguments if needed (skip if we emitted VARARG directly)
2725+ if !inner_last_arg_is_vararg_or_call {
2726+ for ( j, & reg) in call_arg_regs. iter ( ) . enumerate ( ) {
2727+ let target = call_args_start + j as u32 ;
2728+ if reg != target {
2729+ while c. freereg <= target {
2730+ alloc_register ( c) ;
2731+ }
2732+ emit_move ( c, target, reg) ;
26612733 }
2662- emit_move ( c, target, reg) ;
26632734 }
26642735 }
26652736
26662737 // Emit call with "all out" (C=0)
2667- let inner_arg_count = call_arg_exprs. len ( ) ;
2668- let inner_b_param = if inner_is_method {
2669- ( inner_arg_count + 2 ) as u32 // +1 for self, +1 for Lua convention
2738+ // B = number of arguments + 1, or 0 if last arg was vararg/call
2739+ let inner_b_param = if inner_last_arg_is_vararg_or_call {
2740+ 0 // B=0: variable number of args
2741+ } else if inner_is_method {
2742+ ( num_call_args + 2 ) as u32 // +1 for self, +1 for Lua convention
26702743 } else {
2671- ( inner_arg_count + 1 ) as u32
2744+ ( num_call_args + 1 ) as u32
26722745 } ;
26732746 emit (
26742747 c,
@@ -2743,6 +2816,7 @@ pub fn compile_call_expr_with_returns_and_dest(
27432816 // B = number of arguments + 1, or 0 if last arg was "all out" call
27442817 // For method calls, B includes the implicit self parameter
27452818 // C = number of expected return values + 1 (1 means 0 returns, 2 means 1 return, 0 means all returns)
2819+ // SPECIAL: when num_returns = usize::MAX, it means "all out" mode (C=0)
27462820 let arg_count = arg_exprs. len ( ) ;
27472821 let b_param = if last_arg_is_call_all_out {
27482822 0 // B=0: all in
@@ -2751,7 +2825,13 @@ pub fn compile_call_expr_with_returns_and_dest(
27512825 let total_args = if is_method { arg_count + 1 } else { arg_count } ;
27522826 ( total_args + 1 ) as u32
27532827 } ;
2754- let c_param = ( num_returns + 1 ) as u32 ;
2828+ // C=0 means "all out", C=1 means 0 returns, C=2 means 1 return, etc.
2829+ // When caller passes num_returns=usize::MAX, they mean "all out" (C=0)
2830+ let c_param = if num_returns == usize:: MAX {
2831+ 0 // C=0: all out (take all return values)
2832+ } else {
2833+ ( num_returns + 1 ) as u32
2834+ } ;
27552835
27562836 emit (
27572837 c,
@@ -2996,6 +3076,16 @@ fn compile_table_expr_to(
29963076 let mut array_idx = 0 ;
29973077 let values_start = reg + 1 ;
29983078 let mut has_vararg_at_end = false ;
3079+
3080+ // CRITICAL: Pre-reserve registers for array elements BEFORE processing any fields.
3081+ // This prevents hash field value expressions (like `select('#', ...)`) from
3082+ // allocating temporary registers that conflict with array element positions.
3083+ // Without this, `{n = select('#', ...), ...}` would have `select` use reg+1,
3084+ // which should be reserved for the first vararg element.
3085+ let array_values_end = values_start + array_count as u32 ;
3086+ while c. freereg < array_values_end {
3087+ alloc_register ( c) ;
3088+ }
29993089 let mut has_call_at_end = false ;
30003090
30013091 // Process all fields in source order
0 commit comments