Skip to content

Commit dbb3dcd

Browse files
committed
Fix big loop
1 parent 07dfa7a commit dbb3dcd

File tree

4 files changed

+87
-43
lines changed

4 files changed

+87
-43
lines changed

crates/luars/src/lua_vm/execute/arithmetic_instructions.rs

Lines changed: 48 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -140,7 +140,7 @@ pub fn exec_mul(vm: &mut LuaVM, instr: u32, pc: &mut usize, base_ptr: usize) {
140140
}
141141

142142
/// DIV: R[A] = R[B] / R[C]
143-
/// Division always returns float in Lua
143+
/// OPTIMIZED: If both are integers and division is exact, return integer
144144
pub fn exec_div(vm: &mut LuaVM, instr: u32, pc: &mut usize, base_ptr: usize) {
145145
let a = Instruction::get_a(instr) as usize;
146146
let b = Instruction::get_b(instr) as usize;
@@ -151,6 +151,21 @@ pub fn exec_div(vm: &mut LuaVM, instr: u32, pc: &mut usize, base_ptr: usize) {
151151
let left = *reg_base.add(b);
152152
let right = *reg_base.add(c);
153153

154+
let combined_tags = (left.primary | right.primary) & TYPE_MASK;
155+
156+
// Fast path: both integers - check if division is exact
157+
if combined_tags == TAG_INTEGER {
158+
let l = left.secondary as i64;
159+
let r = right.secondary as i64;
160+
if r != 0 && l % r == 0 {
161+
// Exact division - return integer
162+
*vm.register_stack.as_mut_ptr().add(base_ptr + a) = LuaValue::integer(l / r);
163+
*pc += 1;
164+
return;
165+
}
166+
}
167+
168+
// Slow path: convert to float
154169
let left_tag = left.primary & TYPE_MASK;
155170
let right_tag = right.primary & TYPE_MASK;
156171

@@ -224,6 +239,7 @@ pub fn exec_idiv(vm: &mut LuaVM, instr: u32, pc: &mut usize, base_ptr: usize) {
224239
}
225240

226241
/// MOD: R[A] = R[B] % R[C]
242+
/// OPTIMIZED: Returns integer when both operands are integers
227243
pub fn exec_mod(vm: &mut LuaVM, instr: u32, pc: &mut usize, base_ptr: usize) {
228244
let a = Instruction::get_a(instr) as usize;
229245
let b = Instruction::get_b(instr) as usize;
@@ -242,6 +258,7 @@ pub fn exec_mod(vm: &mut LuaVM, instr: u32, pc: &mut usize, base_ptr: usize) {
242258
return; // Mod by zero - let MMBIN handle
243259
}
244260
let l = left.secondary as i64;
261+
// Integer modulo always returns integer
245262
LuaValue::integer(l.rem_euclid(r))
246263
} else {
247264
let left_tag = left.primary & TYPE_MASK;
@@ -263,8 +280,14 @@ pub fn exec_mod(vm: &mut LuaVM, instr: u32, pc: &mut usize, base_ptr: usize) {
263280
return;
264281
};
265282

266-
let result = l_float - (l_float / r_float).floor() * r_float;
267-
LuaValue::number(result)
283+
let float_result = l_float - (l_float / r_float).floor() * r_float;
284+
// Check if result is an integer
285+
if float_result.fract() == 0.0 && float_result.is_finite()
286+
&& float_result >= i64::MIN as f64 && float_result <= i64::MAX as f64 {
287+
LuaValue::integer(float_result as i64)
288+
} else {
289+
LuaValue::number(float_result)
290+
}
268291
};
269292

270293
*vm.register_stack.as_mut_ptr().add(base_ptr + a) = result;
@@ -525,7 +548,7 @@ pub fn exec_mulk(
525548
}
526549

527550
/// MODK: R[A] = R[B] % K[C]
528-
/// OPTIMIZED: Uses cached constants_ptr for direct constant access
551+
/// OPTIMIZED: Returns integer when result is integer
529552
#[inline(always)]
530553
pub fn exec_modk(
531554
vm: &mut LuaVM,
@@ -540,11 +563,9 @@ pub fn exec_modk(
540563

541564
unsafe {
542565
let left = *vm.register_stack.as_ptr().add(base_ptr + b);
543-
544-
// FAST PATH: Direct constant access via cached pointer
545566
let constant = *(*frame_ptr).constants_ptr.add(c);
546567

547-
// Integer % Integer fast path
568+
// Integer % Integer fast path - always returns integer
548569
if left.primary == TAG_INTEGER && constant.primary == TAG_INTEGER {
549570
let r = constant.secondary as i64;
550571
if r == 0 {
@@ -559,10 +580,15 @@ pub fn exec_modk(
559580
return;
560581
}
561582

562-
// Float % Float
583+
// Float % Float - check if result is integer
563584
if let (Some(l), Some(r)) = (left.as_number(), constant.as_number()) {
564-
let result = l - (l / r).floor() * r;
565-
*vm.register_stack.as_mut_ptr().add(base_ptr + a) = LuaValue::number(result);
585+
let float_result = l - (l / r).floor() * r;
586+
if float_result.fract() == 0.0 && float_result.is_finite()
587+
&& float_result >= i64::MIN as f64 && float_result <= i64::MAX as f64 {
588+
*vm.register_stack.as_mut_ptr().add(base_ptr + a) = LuaValue::integer(float_result as i64);
589+
} else {
590+
*vm.register_stack.as_mut_ptr().add(base_ptr + a) = LuaValue::number(float_result);
591+
}
566592
*pc += 1;
567593
}
568594
}
@@ -603,7 +629,7 @@ pub fn exec_powk(
603629
}
604630

605631
/// DIVK: R[A] = R[B] / K[C]
606-
/// OPTIMIZED: Uses cached constants_ptr for direct constant access
632+
/// OPTIMIZED: Returns integer when division is exact
607633
#[inline(always)]
608634
pub fn exec_divk(
609635
vm: &mut LuaVM,
@@ -618,10 +644,19 @@ pub fn exec_divk(
618644

619645
unsafe {
620646
let left = *vm.register_stack.as_ptr().add(base_ptr + b);
621-
622-
// FAST PATH: Direct constant access via cached pointer
623647
let constant = *(*frame_ptr).constants_ptr.add(c);
624648

649+
// Fast path: both integers - check if division is exact
650+
if left.primary == TAG_INTEGER && constant.primary == TAG_INTEGER {
651+
let l = left.secondary as i64;
652+
let r = constant.secondary as i64;
653+
if r != 0 && l % r == 0 {
654+
*vm.register_stack.as_mut_ptr().add(base_ptr + a) = LuaValue::integer(l / r);
655+
*pc += 1;
656+
return;
657+
}
658+
}
659+
625660
let l_float = match left.as_number() {
626661
Some(n) => n,
627662
None => return,

crates/luars/src/lua_vm/execute/control_instructions.rs

Lines changed: 14 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -755,6 +755,8 @@ pub fn exec_gei(vm: &mut LuaVM, instr: u32, pc: &mut usize, base_ptr: usize) ->
755755
/// CALL A B C
756756
/// R[A], ... ,R[A+C-2] := R[A](R[A+1], ... ,R[A+B-1])
757757
/// ULTRA-OPTIMIZED: Minimize overhead for the common case (Lua function, no metamethod)
758+
/// Returns Ok(true) if frame changed (Lua function) and updatestate is needed
759+
/// Returns Ok(false) if frame unchanged (C function) and no updatestate needed
758760
#[inline(always)]
759761
pub fn exec_call(
760762
vm: &mut LuaVM,
@@ -776,7 +778,7 @@ pub fn exec_call(
776778
// Check type tag directly without full pattern match
777779
use crate::lua_value::TAG_FUNCTION;
778780
if (func.primary & crate::lua_value::TYPE_MASK) == TAG_FUNCTION {
779-
return exec_call_lua_function(
781+
exec_call_lua_function(
780782
vm,
781783
func,
782784
a,
@@ -786,12 +788,13 @@ pub fn exec_call(
786788
false,
787789
LuaValue::nil(),
788790
frame_ptr_ptr,
789-
);
791+
)?;
792+
return Ok(()); // Frame changed, need updatestate
790793
}
791794

792795
// Check for CFunction
793796
if func.is_cfunction() {
794-
return exec_call_cfunction(
797+
exec_call_cfunction(
795798
vm,
796799
func,
797800
a,
@@ -801,7 +804,8 @@ pub fn exec_call(
801804
false,
802805
LuaValue::nil(),
803806
frame_ptr_ptr,
804-
);
807+
)?;
808+
return Ok(()); // Frame unchanged, no updatestate needed
805809
}
806810

807811
// Slow path: Check for __call metamethod
@@ -818,7 +822,7 @@ pub fn exec_call(
818822
if let Some(call_func) = vm.table_get_with_meta(&metatable, &call_key) {
819823
if call_func.is_callable() {
820824
if call_func.is_cfunction() {
821-
return exec_call_cfunction(
825+
exec_call_cfunction(
822826
vm,
823827
call_func,
824828
a,
@@ -828,9 +832,10 @@ pub fn exec_call(
828832
true,
829833
func,
830834
frame_ptr_ptr,
831-
);
835+
)?;
836+
return Ok(()); // C function, no updatestate
832837
} else {
833-
return exec_call_lua_function(
838+
exec_call_lua_function(
834839
vm,
835840
call_func,
836841
a,
@@ -840,7 +845,8 @@ pub fn exec_call(
840845
true,
841846
func,
842847
frame_ptr_ptr,
843-
);
848+
)?;
849+
return Ok(()); // Lua function, need updatestate
844850
}
845851
}
846852
}

crates/luars/src/lua_vm/execute/loop_instructions.rs

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -130,7 +130,7 @@ pub fn exec_forprep(vm: &mut LuaVM, instr: u32, pc: &mut usize, base_ptr: usize)
130130
/// R[A]+=R[A+2];
131131
/// if R[A] <?= R[A+1] then { pc-=Bx; R[A+3]=R[A] }
132132
///
133-
/// ULTRA-OPTIMIZED: Only check step type (like Lua C), use chgivalue pattern
133+
/// ULTRA-OPTIMIZED: Check idx type (set by FORPREP), use chgivalue pattern
134134
#[inline(always)]
135135
#[allow(dead_code)]
136136
pub fn exec_forloop(vm: &mut LuaVM, instr: u32, pc: &mut usize, base_ptr: usize) -> LuaResult<()> {
@@ -140,17 +140,17 @@ pub fn exec_forloop(vm: &mut LuaVM, instr: u32, pc: &mut usize, base_ptr: usize)
140140
unsafe {
141141
let reg_base = vm.register_stack.as_mut_ptr().add(base_ptr + a);
142142

143-
// Only check step type - like Lua C's ttisinteger(s2v(ra + 2))
144-
let step = *reg_base.add(2);
143+
// Check idx type - FORPREP sets this to integer only if ALL of init/limit/step are integers
144+
let idx = *reg_base;
145145

146-
if step.primary == TAG_INTEGER {
147-
// Integer loop - step is integer, so idx and counter must be too (set by FORPREP)
146+
if (idx.primary & TYPE_MASK) == TAG_INTEGER {
147+
// Integer loop - idx and counter are integers (set by FORPREP)
148148
let count = (*reg_base.add(1)).secondary; // counter as u64
149149

150150
if count > 0 {
151-
let idx = (*reg_base).secondary as i64;
152-
let step_i = step.secondary as i64;
153-
let new_idx = idx.wrapping_add(step_i);
151+
let idx_i = idx.secondary as i64;
152+
let step_i = (*reg_base.add(2)).secondary as i64;
153+
let new_idx = idx_i.wrapping_add(step_i);
154154

155155
// chgivalue pattern - only update secondary (value), primary (type) stays same
156156
(*reg_base.add(1)).secondary = count - 1; // counter--

crates/luars/src/lua_vm/execute/mod.rs

Lines changed: 17 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ pub use upvalue_instructions::*;
1818

1919
use super::{Instruction, LuaError, LuaResult, LuaVM, OpCode};
2020
use crate::LuaValue;
21-
use crate::lua_value::TAG_INTEGER;
21+
use crate::lua_value::{TAG_INTEGER, TYPE_MASK};
2222
use crate::lua_vm::LuaCallFrame;
2323

2424
/// Save current pc to frame (like Lua C's savepc macro)
@@ -413,15 +413,16 @@ pub fn luavm_execute(vm: &mut LuaVM) -> LuaResult<LuaValue> {
413413
let bx = Instruction::get_bx(instr) as usize;
414414
unsafe {
415415
let reg_base = vm.register_stack.as_mut_ptr().add(base_ptr + a);
416-
let step = *reg_base.add(2);
417-
418-
// Integer loop (like Lua C)
419-
if step.primary == TAG_INTEGER {
416+
let idx = *reg_base;
417+
418+
// Integer loop: check if idx is integer (FORPREP sets this correctly)
419+
// If any of init/limit/step was float, FORPREP uses float mode
420+
if (idx.primary & TYPE_MASK) == TAG_INTEGER {
420421
let count = (*reg_base.add(1)).secondary;
421422
if count > 0 {
422-
let idx = (*reg_base).secondary as i64;
423-
let step_i = step.secondary as i64;
424-
let new_idx = idx.wrapping_add(step_i);
423+
let idx_i = idx.secondary as i64;
424+
let step_i = (*reg_base.add(2)).secondary as i64;
425+
let new_idx = idx_i.wrapping_add(step_i);
425426
(*reg_base.add(1)).secondary = count - 1;
426427
(*reg_base).secondary = new_idx as u64;
427428
(*reg_base.add(3)).secondary = new_idx as u64;
@@ -499,12 +500,14 @@ pub fn luavm_execute(vm: &mut LuaVM) -> LuaResult<LuaValue> {
499500
OpCode::Call => {
500501
// Save current pc to frame before call (so return knows where to resume)
501502
savepc!(frame_ptr, pc);
502-
if let Err(e) = exec_call(vm, instr, &mut frame_ptr) {
503-
return Err(e);
504-
}
505-
// Reload state from new frame
506-
unsafe {
507-
updatestate(frame_ptr, &mut pc, &mut code_ptr, &mut base_ptr);
503+
match exec_call(vm, instr, &mut frame_ptr) {
504+
Ok(_) => {
505+
// Lua function: frame changed, reload state from new frame
506+
unsafe {
507+
updatestate(frame_ptr, &mut pc, &mut code_ptr, &mut base_ptr);
508+
}
509+
}
510+
Err(e) => return Err(e),
508511
}
509512
continue 'mainloop;
510513
}

0 commit comments

Comments
 (0)