@@ -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) ]
1314pub 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) ]
3739pub 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) ]
7274pub 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;
0 commit comments