Skip to content

Commit c7fa53f

Browse files
committed
fix dead loop
1 parent 174cad5 commit c7fa53f

File tree

1 file changed

+28
-191
lines changed

1 file changed

+28
-191
lines changed

crates/luars/src/compiler/expr.rs

Lines changed: 28 additions & 191 deletions
Original file line numberDiff line numberDiff line change
@@ -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

Comments
 (0)