Skip to content

Commit a58e33b

Browse files
committed
update
1 parent 7217a95 commit a58e33b

File tree

4 files changed

+130
-76
lines changed

4 files changed

+130
-76
lines changed

crates/luars/src/lib_registry.rs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -212,6 +212,17 @@ pub fn get_args(vm: &LuaVM) -> Vec<LuaValue> {
212212
(1..top).map(|i| vm.register_stack[base_ptr + i]).collect()
213213
}
214214

215+
/// Iterate over all arguments without allocation
216+
/// Returns an iterator that yields (1-based index, value) pairs
217+
#[inline(always)]
218+
pub fn args_iter(vm: &LuaVM) -> impl Iterator<Item = (usize, LuaValue)> + '_ {
219+
let frame = vm.current_frame();
220+
let base_ptr = frame.base_ptr as usize;
221+
let top = frame.top as usize;
222+
223+
(1..top).map(move |i| (i, vm.register_stack[base_ptr + i]))
224+
}
225+
215226
/// Helper to get a specific argument
216227
/// 1 based index
217228
#[inline(always)]

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

Lines changed: 44 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -446,13 +446,54 @@ pub fn luavm_execute(vm: &mut LuaVM) -> LuaResult<LuaValue> {
446446
continue 'mainloop;
447447
}
448448

449-
// ============ Upvalue operations (inline simple ones) ============
449+
// ============ Upvalue operations (inline for performance) ============
450450
OpCode::GetUpval => {
451-
exec_getupval(vm, instr, frame_ptr, base_ptr);
451+
// INLINED GETUPVAL: R[A] := UpValue[B]
452+
let a = Instruction::get_a(instr) as usize;
453+
let b = Instruction::get_b(instr) as usize;
454+
455+
unsafe {
456+
// Get function's upvalue list pointer
457+
let func_id = (*frame_ptr).get_function_id_unchecked();
458+
let func_ref = vm.object_pool.get_function_unchecked(func_id);
459+
let upvalue_id = *func_ref.upvalues.get_unchecked(b);
460+
461+
// Read upvalue value directly
462+
let uv = vm.object_pool.get_upvalue_unchecked(upvalue_id);
463+
let value = match &uv.state {
464+
crate::gc::UpvalueState::Open { stack_index } => {
465+
*vm.register_stack.get_unchecked(*stack_index)
466+
}
467+
crate::gc::UpvalueState::Closed(val) => *val,
468+
};
469+
470+
*vm.register_stack.get_unchecked_mut(base_ptr + a) = value;
471+
}
452472
continue 'mainloop;
453473
}
454474
OpCode::SetUpval => {
455-
exec_setupval(vm, instr, frame_ptr, base_ptr);
475+
// INLINED SETUPVAL: UpValue[B] := R[A]
476+
let a = Instruction::get_a(instr) as usize;
477+
let b = Instruction::get_b(instr) as usize;
478+
479+
unsafe {
480+
// Get the value to write
481+
let value = *vm.register_stack.get_unchecked(base_ptr + a);
482+
483+
// Get function's upvalue list pointer
484+
let func_id = (*frame_ptr).get_function_id_unchecked();
485+
let func_ref = vm.object_pool.get_function_unchecked(func_id);
486+
let upvalue_id = *func_ref.upvalues.get_unchecked(b);
487+
488+
// Write upvalue value directly
489+
let uv = vm.object_pool.get_upvalue_mut_unchecked(upvalue_id);
490+
match &mut uv.state {
491+
crate::gc::UpvalueState::Open { stack_index } => {
492+
*vm.register_stack.get_unchecked_mut(*stack_index) = value;
493+
}
494+
crate::gc::UpvalueState::Closed(val) => *val = value,
495+
};
496+
}
456497
continue 'mainloop;
457498
}
458499

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

Lines changed: 32 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ use crate::{
99
/// GETUPVAL A B
1010
/// R[A] := UpValue[B]
1111
/// Get upvalue from the closure's upvalue list
12+
#[allow(dead_code)]
1213
#[inline(always)]
1314
pub fn exec_getupval(vm: &mut LuaVM, instr: u32, frame_ptr: *mut LuaCallFrame, base_ptr: usize) {
1415
let a = Instruction::get_a(instr) as usize;
@@ -33,6 +34,7 @@ pub fn exec_getupval(vm: &mut LuaVM, instr: u32, frame_ptr: *mut LuaCallFrame, b
3334
/// SETUPVAL A B
3435
/// UpValue[B] := R[A]
3536
/// Set upvalue in the closure's upvalue list
37+
#[allow(dead_code)]
3638
#[inline(always)]
3739
pub fn exec_setupval(vm: &mut LuaVM, instr: u32, frame_ptr: *mut LuaCallFrame, base_ptr: usize) {
3840
let a = Instruction::get_a(instr) as usize;
@@ -67,7 +69,7 @@ pub fn exec_close(vm: &mut LuaVM, instr: u32, base_ptr: usize) {
6769

6870
/// CLOSURE A Bx
6971
/// R[A] := closure(KPROTO[Bx])
70-
/// OPTIMIZED: Fast path for closures without upvalues
72+
/// OPTIMIZED: Fast path for closures without upvalues, avoid unnecessary clones
7173
#[inline(always)]
7274
pub fn exec_closure(
7375
vm: &mut LuaVM,
@@ -80,21 +82,19 @@ pub fn exec_closure(
8082
let a = Instruction::get_a(instr) as usize;
8183
let bx = Instruction::get_bx(instr) as usize;
8284

85+
// Get prototype and parent upvalues without cloning upvalues early
8386
let func_id = unsafe { (*frame_ptr).get_function_id_unchecked() };
84-
let func_ref = vm.object_pool.get_function(func_id);
85-
let (proto, parent_upvalues) = if let Some(f) = func_ref {
86-
let p = f.chunk.child_protos.get(bx).cloned();
87-
if let Some(proto) = p {
88-
(proto, f.upvalues.clone())
89-
} else {
90-
return Err(vm.error(format!("Invalid prototype index: {}", bx)));
91-
}
92-
} else {
93-
return Err(vm.error("Invalid function reference".to_string()));
87+
let func_ref = unsafe { vm.object_pool.get_function_unchecked(func_id) };
88+
89+
let proto = func_ref.chunk.child_protos.get(bx).cloned();
90+
let proto = match proto {
91+
Some(p) => p,
92+
None => return Err(vm.error(format!("Invalid prototype index: {}", bx))),
9493
};
9594

9695
// FAST PATH: No upvalues (most common for simple lambdas)
97-
if proto.upvalue_descs.is_empty() {
96+
let upvalue_count = proto.upvalue_descs.len();
97+
if upvalue_count == 0 {
9898
let closure = vm.create_function(proto, Vec::new());
9999
unsafe {
100100
*vm.register_stack.get_unchecked_mut(base_ptr + a) = closure;
@@ -103,48 +103,46 @@ pub fn exec_closure(
103103
return Ok(());
104104
}
105105

106-
// Get upvalue descriptors from the prototype
107-
let upvalue_descs = proto.upvalue_descs.clone();
108-
109-
// Create upvalues for the new closure based on descriptors
110-
let mut upvalue_ids: Vec<UpvalueId> = Vec::with_capacity(upvalue_descs.len());
111-
let mut new_open_upvalue_ids: Vec<UpvalueId> = Vec::new();
106+
// Pre-allocate upvalue_ids with exact capacity (no separate new_open_upvalue_ids Vec)
107+
let mut upvalue_ids: Vec<UpvalueId> = Vec::with_capacity(upvalue_count);
112108

113-
for desc in upvalue_descs.iter() {
109+
// Process upvalue descriptors without cloning
110+
for desc in proto.upvalue_descs.iter() {
114111
if desc.is_local {
115112
// Upvalue refers to a register in current function
116-
// Calculate absolute stack index for this upvalue
117113
let stack_index = base_ptr + desc.index as usize;
118114

119-
// Check if this upvalue is already open
120-
let existing = vm.open_upvalues.iter().find(|uv_id| {
121-
vm.object_pool
122-
.get_upvalue(**uv_id)
123-
.map(|uv| uv.points_to_index(stack_index))
124-
.unwrap_or(false)
125-
});
115+
// Check if this upvalue is already open - use linear search (usually small list)
116+
let mut found = None;
117+
for &uv_id in vm.open_upvalues.iter() {
118+
// SAFETY: upvalue IDs in open_upvalues are always valid
119+
let uv = unsafe { vm.object_pool.get_upvalue_unchecked(uv_id) };
120+
if uv.points_to_index(stack_index) {
121+
found = Some(uv_id);
122+
break;
123+
}
124+
}
126125

127-
if let Some(&existing_uv_id) = existing {
126+
if let Some(existing_uv_id) = found {
128127
upvalue_ids.push(existing_uv_id);
129128
} else {
130-
// Create new open upvalue using absolute stack index
129+
// Create new open upvalue and add to open list directly
131130
let new_uv_id = vm.object_pool.create_upvalue_open(stack_index);
132131
upvalue_ids.push(new_uv_id);
133-
new_open_upvalue_ids.push(new_uv_id);
132+
vm.open_upvalues.push(new_uv_id);
134133
}
135134
} else {
136135
// Upvalue refers to an upvalue in the enclosing function
137-
if let Some(&parent_uv_id) = parent_upvalues.get(desc.index as usize) {
136+
// Need to get parent upvalues again (func_ref may be invalidated by alloc)
137+
let parent_func = unsafe { vm.object_pool.get_function_unchecked(func_id) };
138+
if let Some(&parent_uv_id) = parent_func.upvalues.get(desc.index as usize) {
138139
upvalue_ids.push(parent_uv_id);
139140
} else {
140141
return Err(vm.error(format!("Invalid upvalue index in parent: {}", desc.index)));
141142
}
142143
}
143144
}
144145

145-
// Add all new upvalues to the open list
146-
vm.open_upvalues.extend(new_open_upvalue_ids);
147-
148146
let closure = vm.create_function(proto, upvalue_ids);
149147
unsafe {
150148
*vm.register_stack.get_unchecked_mut(base_ptr + a) = closure;

crates/luars/src/stdlib/math.rs

Lines changed: 43 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
// log, max, min, modf, rad, random, randomseed, sin, sqrt, tan, tointeger,
44
// type, ult, pi, huge, maxinteger, mininteger
55

6-
use crate::lib_registry::{LibraryModule, get_arg, get_args, require_arg};
6+
use crate::lib_registry::{LibraryModule, get_arg, require_arg};
77
use crate::lua_value::{LuaValue, LuaValueKind, MultiValue};
88
use crate::lua_vm::{LuaResult, LuaVM};
99

@@ -118,57 +118,61 @@ fn math_log(vm: &mut LuaVM) -> LuaResult<MultiValue> {
118118
}
119119

120120
fn math_max(vm: &mut LuaVM) -> LuaResult<MultiValue> {
121-
let args = get_args(vm);
122-
123-
if args.is_empty() {
121+
use crate::lib_registry::{arg_count, get_arg};
122+
123+
let argc = arg_count(vm);
124+
if argc == 0 {
124125
return Err(vm.error("bad argument to 'math.max' (value expected)".to_string()));
125126
}
126-
127-
// Keep the original value and its index to preserve type (integer vs float)
128-
let mut max_idx = 0;
129-
let mut max_val = args[0]
130-
.as_number()
127+
128+
// Get first argument
129+
let first = get_arg(vm, 1).unwrap();
130+
let mut max_val = first.as_number()
131131
.ok_or_else(|| vm.error("bad argument to 'math.max' (number expected)".to_string()))?;
132-
133-
for (i, arg) in args.iter().enumerate().skip(1) {
134-
let val = arg
135-
.as_number()
136-
.ok_or_else(|| vm.error("bad argument to 'math.max' (number expected)".to_string()))?;
137-
if val > max_val {
138-
max_val = val;
139-
max_idx = i;
132+
let mut max_arg = first;
133+
134+
// Compare with rest
135+
for i in 2..=argc {
136+
if let Some(arg) = get_arg(vm, i) {
137+
let val = arg.as_number()
138+
.ok_or_else(|| vm.error("bad argument to 'math.max' (number expected)".to_string()))?;
139+
if val > max_val {
140+
max_val = val;
141+
max_arg = arg;
142+
}
140143
}
141144
}
142-
143-
// Return the original value (preserves integer/float type)
144-
Ok(MultiValue::single(args[max_idx]))
145+
146+
Ok(MultiValue::single(max_arg))
145147
}
146148

147149
fn math_min(vm: &mut LuaVM) -> LuaResult<MultiValue> {
148-
let args = get_args(vm);
149-
150-
if args.is_empty() {
150+
use crate::lib_registry::{arg_count, get_arg};
151+
152+
let argc = arg_count(vm);
153+
if argc == 0 {
151154
return Err(vm.error("bad argument to 'math.min' (value expected)".to_string()));
152155
}
153-
154-
// Keep the original value and its index to preserve type (integer vs float)
155-
let mut min_idx = 0;
156-
let mut min_val = args[0]
157-
.as_number()
156+
157+
// Get first argument
158+
let first = get_arg(vm, 1).unwrap();
159+
let mut min_val = first.as_number()
158160
.ok_or_else(|| vm.error("bad argument to 'math.min' (number expected)".to_string()))?;
159-
160-
for (i, arg) in args.iter().enumerate().skip(1) {
161-
let val = arg
162-
.as_number()
163-
.ok_or_else(|| vm.error("bad argument to 'math.min' (number expected)".to_string()))?;
164-
if val < min_val {
165-
min_val = val;
166-
min_idx = i;
161+
let mut min_arg = first;
162+
163+
// Compare with rest
164+
for i in 2..=argc {
165+
if let Some(arg) = get_arg(vm, i) {
166+
let val = arg.as_number()
167+
.ok_or_else(|| vm.error("bad argument to 'math.min' (number expected)".to_string()))?;
168+
if val < min_val {
169+
min_val = val;
170+
min_arg = arg;
171+
}
167172
}
168173
}
169-
170-
// Return the original value (preserves integer/float type)
171-
Ok(MultiValue::single(args[min_idx]))
174+
175+
Ok(MultiValue::single(min_arg))
172176
}
173177

174178
fn math_modf(vm: &mut LuaVM) -> LuaResult<MultiValue> {

0 commit comments

Comments
 (0)