|
1 | 1 | /// Control flow instructions |
2 | 2 | /// |
3 | | -/// These instructions handle function calls, returns, jumps, and coroutine operations. |
| 3 | +/// These instructions handle function calls, returns, and comparisons. |
4 | 4 | use crate::lua_value::LuaValueKind; |
5 | 5 | use crate::lua_vm::{LuaCallFrame, LuaError, LuaResult, LuaVM}; |
6 | 6 | use crate::{FunctionId, LuaValue}; |
7 | | -use crate::{get_a, get_b, get_c, get_k, get_sb, get_sj}; |
| 7 | +use crate::{get_a, get_b, get_c, get_k, get_sb}; |
8 | 8 |
|
9 | 9 | /// RETURN A B C k |
10 | 10 | /// return R[A], ... ,R[A+B-2] |
@@ -135,47 +135,8 @@ pub fn exec_return( |
135 | 135 | } |
136 | 136 | } |
137 | 137 |
|
138 | | -// ============ Jump Instructions ============ |
139 | | - |
140 | | -/// JMP sJ |
141 | | -/// pc += sJ |
142 | | -#[allow(dead_code)] |
143 | | -#[inline(always)] |
144 | | -pub fn exec_jmp(instr: u32, pc: &mut usize) { |
145 | | - let sj = get_sj!(instr); |
146 | | - |
147 | | - // PC already incremented by dispatcher, so we add offset directly |
148 | | - *pc = (*pc as i32 + sj) as usize; |
149 | | -} |
150 | | - |
151 | 138 | // ============ Test Instructions ============ |
152 | 139 |
|
153 | | -/// TEST A k |
154 | | -/// if (not R[A] == k) then pc++ |
155 | | -/// ULTRA-OPTIMIZED: Direct type tag check, single branch |
156 | | -#[allow(dead_code)] |
157 | | -#[inline(always)] |
158 | | -pub fn exec_test(vm: &mut LuaVM, instr: u32, pc: &mut usize, base_ptr: usize) { |
159 | | - let a = get_a!(instr); |
160 | | - let k = get_k!(instr); |
161 | | - |
162 | | - unsafe { |
163 | | - // OPTIMIZATION: Direct unsafe access and type tag comparison |
164 | | - let value = *vm.register_stack.as_ptr().add(base_ptr + a); |
165 | | - |
166 | | - // OPTIMIZATION: Fast truthiness check using type tags |
167 | | - // nil = TAG_NIL, false = VALUE_FALSE |
168 | | - // Only nil and false are falsy |
169 | | - use crate::lua_value::{TAG_NIL, VALUE_FALSE}; |
170 | | - let is_truthy = value.primary != TAG_NIL && value.primary != VALUE_FALSE; |
171 | | - |
172 | | - // If (not value) == k, skip next instruction |
173 | | - if !is_truthy == k { |
174 | | - *pc += 1; |
175 | | - } |
176 | | - } |
177 | | -} |
178 | | - |
179 | 140 | /// TESTSET A B k |
180 | 141 | /// if (not R[B] == k) then R[A] := R[B] else pc++ |
181 | 142 | /// ULTRA-OPTIMIZED: Direct type tag check, single branch |
@@ -1230,193 +1191,6 @@ fn exec_call_lua_vararg( |
1230 | 1191 | Ok(()) |
1231 | 1192 | } |
1232 | 1193 |
|
1233 | | -/// Slow path for Lua function calls with metamethod |
1234 | | -/// OPTIMIZED: Minimize branches and memory operations for common case |
1235 | | -#[inline(always)] |
1236 | | -fn exec_call_lua_function( |
1237 | | - vm: &mut LuaVM, |
1238 | | - func: LuaValue, |
1239 | | - a: usize, |
1240 | | - b: usize, |
1241 | | - c: usize, |
1242 | | - caller_base: usize, |
1243 | | - use_call_metamethod: bool, |
1244 | | - call_metamethod_self: LuaValue, |
1245 | | - frame_ptr_ptr: &mut *mut LuaCallFrame, // Use passed frame_ptr! |
1246 | | -) -> LuaResult<()> { |
1247 | | - // Get function ID - FAST PATH: assume valid function |
1248 | | - let func_id = unsafe { func.as_function_id().unwrap_unchecked() }; |
1249 | | - |
1250 | | - // Extract chunk info from ObjectPool - use unchecked for hot path |
1251 | | - let func_ref = unsafe { vm.object_pool.get_function_unchecked(func_id) }; |
1252 | | - let chunk = func_ref.lua_chunk(); |
1253 | | - |
1254 | | - let (max_stack_size, num_params, is_vararg, code_ptr, constants_ptr, upvalues_ptr) = ( |
1255 | | - chunk.max_stack_size, |
1256 | | - chunk.param_count, |
1257 | | - chunk.is_vararg, |
1258 | | - chunk.code.as_ptr(), |
1259 | | - chunk.constants.as_ptr(), |
1260 | | - func_ref.upvalues.as_ptr(), |
1261 | | - ); |
1262 | | - |
1263 | | - // Calculate argument count - use frame_ptr directly! |
1264 | | - let arg_count = if b == 0 { |
1265 | | - unsafe { (**frame_ptr_ptr).top.saturating_sub((a + 1) as u32) as usize } |
1266 | | - } else { |
1267 | | - unsafe { |
1268 | | - (**frame_ptr_ptr).top = (a + b) as u32; |
1269 | | - } |
1270 | | - b - 1 |
1271 | | - }; |
1272 | | - |
1273 | | - let return_count = if c == 0 { usize::MAX } else { c - 1 }; |
1274 | | - |
1275 | | - // Zero-copy: new frame base = R[A+1] |
1276 | | - let new_base = caller_base + a + 1; |
1277 | | - |
1278 | | - // Check if this call would overwrite parent frame's vararg |
1279 | | - // If the parent frame has vararg at vararg_start..vararg_start+vararg_count, |
1280 | | - // and new_base + max_stack_size > vararg_start, we need to relocate vararg |
1281 | | - let parent_frame = unsafe { &mut **frame_ptr_ptr }; |
1282 | | - let parent_vararg_start = parent_frame.get_vararg_start(); |
1283 | | - let parent_vararg_count = parent_frame.get_vararg_count(); |
1284 | | - |
1285 | | - if parent_vararg_count > 0 { |
1286 | | - let new_frame_end = new_base + max_stack_size; |
1287 | | - if new_frame_end > parent_vararg_start { |
1288 | | - // Vararg would be overwritten! Relocate it to after new frame's stack |
1289 | | - let new_vararg_start = new_frame_end; |
1290 | | - let required_capacity = new_vararg_start + parent_vararg_count; |
1291 | | - vm.ensure_stack_capacity(required_capacity); |
1292 | | - if vm.register_stack.len() < required_capacity { |
1293 | | - vm.register_stack.resize(required_capacity, LuaValue::nil()); |
1294 | | - } |
1295 | | - |
1296 | | - // Copy vararg values to new location (copy forward, no overlap issue since new > old) |
1297 | | - for i in 0..parent_vararg_count { |
1298 | | - vm.register_stack[new_vararg_start + i] = |
1299 | | - vm.register_stack[parent_vararg_start + i]; |
1300 | | - } |
1301 | | - |
1302 | | - // Update parent frame's vararg position |
1303 | | - // Need to get mutable reference again after potential reallocation |
1304 | | - let parent_frame = unsafe { &mut **frame_ptr_ptr }; |
1305 | | - parent_frame.set_vararg(new_vararg_start, parent_vararg_count); |
1306 | | - } |
1307 | | - } |
1308 | | - |
1309 | | - // FAST PATH: No metamethod, no vararg (most common case) |
1310 | | - if !use_call_metamethod && !is_vararg { |
1311 | | - // Simple case: just ensure capacity and push frame |
1312 | | - let required_capacity = new_base + max_stack_size; |
1313 | | - |
1314 | | - // Ensure capacity - single branch |
1315 | | - if vm.register_stack.len() < required_capacity { |
1316 | | - vm.register_stack.resize(required_capacity, LuaValue::nil()); |
1317 | | - } |
1318 | | - |
1319 | | - // LIKE LUA C: Only fill missing arguments (arg_count < num_params) |
1320 | | - // NOT all stack slots! This is the key optimization. |
1321 | | - // For add(a,b) called with add(1,5): arg_count=2, num_params=2 → no fill! |
1322 | | - if arg_count < num_params { |
1323 | | - unsafe { |
1324 | | - let reg_ptr = vm.register_stack.as_mut_ptr().add(new_base); |
1325 | | - let nil_val = LuaValue::nil(); |
1326 | | - for i in arg_count..num_params { |
1327 | | - *reg_ptr.add(i) = nil_val; |
1328 | | - } |
1329 | | - } |
1330 | | - } |
1331 | | - |
1332 | | - // Create and push new frame - inline nresults calculation |
1333 | | - let nresults = if c == 0 { -1i16 } else { (c - 1) as i16 }; |
1334 | | - let new_frame = LuaCallFrame::new_lua_function( |
1335 | | - func_id, |
1336 | | - code_ptr, |
1337 | | - constants_ptr, |
1338 | | - upvalues_ptr, |
1339 | | - new_base, |
1340 | | - arg_count, // top = number of arguments |
1341 | | - a, // result_reg |
1342 | | - nresults, |
1343 | | - max_stack_size, |
1344 | | - ); |
1345 | | - |
1346 | | - *frame_ptr_ptr = vm.push_frame(new_frame); |
1347 | | - return Ok(()); |
1348 | | - } |
1349 | | - |
1350 | | - // SLOW PATH: Handle metamethod or vararg |
1351 | | - let actual_arg_count = if use_call_metamethod { |
1352 | | - arg_count + 1 |
1353 | | - } else { |
1354 | | - arg_count |
1355 | | - }; |
1356 | | - let actual_stack_size = max_stack_size.max(actual_arg_count); |
1357 | | - let total_stack_size = if is_vararg && actual_arg_count > 0 { |
1358 | | - actual_stack_size + actual_arg_count |
1359 | | - } else { |
1360 | | - actual_stack_size |
1361 | | - }; |
1362 | | - |
1363 | | - // Ensure stack capacity |
1364 | | - let required_capacity = new_base + total_stack_size; |
1365 | | - if vm.register_stack.len() < required_capacity { |
1366 | | - vm.ensure_stack_capacity(required_capacity); |
1367 | | - vm.register_stack.resize(required_capacity, LuaValue::nil()); |
1368 | | - } |
1369 | | - |
1370 | | - // Initialize registers |
1371 | | - unsafe { |
1372 | | - let reg_ptr = vm.register_stack.as_mut_ptr(); |
1373 | | - let nil_val = LuaValue::nil(); |
1374 | | - |
1375 | | - // Initialize local variable slots beyond arguments |
1376 | | - for i in actual_arg_count..actual_stack_size { |
1377 | | - *reg_ptr.add(new_base + i) = nil_val; |
1378 | | - } |
1379 | | - |
1380 | | - // Vararg extra space |
1381 | | - if is_vararg && actual_arg_count > 0 { |
1382 | | - for i in actual_stack_size..total_stack_size { |
1383 | | - *reg_ptr.add(new_base + i) = nil_val; |
1384 | | - } |
1385 | | - } |
1386 | | - |
1387 | | - // __call metamethod: shift arguments and insert self |
1388 | | - if use_call_metamethod && arg_count > 0 { |
1389 | | - for i in (0..arg_count).rev() { |
1390 | | - *reg_ptr.add(new_base + 1 + i) = *reg_ptr.add(new_base + i); |
1391 | | - } |
1392 | | - *reg_ptr.add(new_base) = call_metamethod_self; |
1393 | | - } else if use_call_metamethod { |
1394 | | - *reg_ptr.add(new_base) = call_metamethod_self; |
1395 | | - } |
1396 | | - } |
1397 | | - |
1398 | | - // Create and push new frame |
1399 | | - let nresults = if return_count == usize::MAX { |
1400 | | - -1i16 |
1401 | | - } else { |
1402 | | - return_count as i16 |
1403 | | - }; |
1404 | | - let new_frame = LuaCallFrame::new_lua_function( |
1405 | | - func_id, |
1406 | | - code_ptr, |
1407 | | - constants_ptr, |
1408 | | - upvalues_ptr, |
1409 | | - new_base, |
1410 | | - actual_arg_count, // top = number of arguments |
1411 | | - a, // result_reg |
1412 | | - nresults, |
1413 | | - max_stack_size, |
1414 | | - ); |
1415 | | - |
1416 | | - *frame_ptr_ptr = vm.push_frame(new_frame); |
1417 | | - Ok(()) |
1418 | | -} |
1419 | | - |
1420 | 1194 | /// Execute a C closure call (native function with upvalues) |
1421 | 1195 | #[inline(always)] |
1422 | 1196 | fn exec_call_c_closure( |
@@ -2109,120 +1883,3 @@ pub fn exec_tailcall( |
2109 | 1883 | _ => Err(vm.error(format!("attempt to call a {} value", func.type_name()))), |
2110 | 1884 | } |
2111 | 1885 | } |
2112 | | - |
2113 | | -/// RETURN0 |
2114 | | -/// return (no values) |
2115 | | -/// ULTRA-OPTIMIZED: Minimal work for the common case (Lua->Lua call with no upvalues) |
2116 | | -/// NOTE: Currently inlined in main loop for performance, but kept for potential future use |
2117 | | -#[inline(always)] |
2118 | | -#[allow(dead_code)] |
2119 | | -pub fn exec_return0( |
2120 | | - vm: &mut LuaVM, |
2121 | | - _instr: u32, |
2122 | | - frame_ptr_ptr: &mut *mut LuaCallFrame, |
2123 | | -) -> LuaResult<()> { |
2124 | | - // Like Lua C: check if we have a Lua caller (most common case) |
2125 | | - if vm.frame_count > 1 { |
2126 | | - // FAST PATH: Calculate caller frame pointer BEFORE pop |
2127 | | - let caller_ptr = unsafe { vm.frames.as_mut_ptr().add(vm.frame_count - 2) }; |
2128 | | - |
2129 | | - // Pop frame - just decrement counter (like Lua C: L->ci = ci->previous) |
2130 | | - vm.frame_count -= 1; |
2131 | | - |
2132 | | - // Check if caller is Lua function |
2133 | | - if unsafe { (*caller_ptr).is_lua() } { |
2134 | | - // Get info we need |
2135 | | - let (result_reg, num_results) = unsafe { |
2136 | | - ( |
2137 | | - (**frame_ptr_ptr).get_result_reg(), |
2138 | | - (**frame_ptr_ptr).get_num_results(), |
2139 | | - ) |
2140 | | - }; |
2141 | | - |
2142 | | - // Update frame_ptr to caller |
2143 | | - *frame_ptr_ptr = caller_ptr; |
2144 | | - |
2145 | | - // Only fill nil if caller expects results |
2146 | | - // Like Lua C: for (nres = ci->nresults; l_unlikely(nres > 0); nres--) |
2147 | | - if num_results > 0 && num_results != usize::MAX { |
2148 | | - let caller_base = unsafe { (*caller_ptr).base_ptr } as usize; |
2149 | | - let dest_base = caller_base + result_reg; |
2150 | | - unsafe { |
2151 | | - let reg_ptr = vm.register_stack.as_mut_ptr(); |
2152 | | - let nil_val = LuaValue::nil(); |
2153 | | - for i in 0..num_results { |
2154 | | - *reg_ptr.add(dest_base + i) = nil_val; |
2155 | | - } |
2156 | | - } |
2157 | | - } |
2158 | | - |
2159 | | - return Ok(()); |
2160 | | - } else { |
2161 | | - // C function caller |
2162 | | - *frame_ptr_ptr = caller_ptr; |
2163 | | - vm.return_values.clear(); |
2164 | | - return Err(LuaError::Exit); |
2165 | | - } |
2166 | | - } |
2167 | | - |
2168 | | - // No caller - exit VM |
2169 | | - vm.frame_count -= 1; |
2170 | | - vm.return_values.clear(); |
2171 | | - Err(LuaError::Exit) |
2172 | | -} |
2173 | | - |
2174 | | -/// RETURN1 A |
2175 | | -/// return R[A] |
2176 | | -/// ULTRA-OPTIMIZED: Minimal work for single-value return (most common case) |
2177 | | -/// NOTE: Currently inlined in main loop for performance, but kept for potential future use |
2178 | | -#[inline(always)] |
2179 | | -#[allow(dead_code)] |
2180 | | -pub fn exec_return1( |
2181 | | - vm: &mut LuaVM, |
2182 | | - instr: u32, |
2183 | | - frame_ptr_ptr: &mut *mut LuaCallFrame, |
2184 | | -) -> LuaResult<()> { |
2185 | | - let a = get_a!(instr); |
2186 | | - |
2187 | | - // Get base_ptr and return value FIRST |
2188 | | - let base_ptr = unsafe { (**frame_ptr_ptr).base_ptr } as usize; |
2189 | | - let return_value = unsafe { *vm.register_stack.get_unchecked(base_ptr + a) }; |
2190 | | - |
2191 | | - // Like Lua C: check if we have a Lua caller (most common case) |
2192 | | - if vm.frame_count > 1 { |
2193 | | - // FAST PATH: Calculate caller frame pointer BEFORE pop |
2194 | | - let caller_ptr = unsafe { vm.frames.as_mut_ptr().add(vm.frame_count - 2) }; |
2195 | | - |
2196 | | - // Pop frame - just decrement counter |
2197 | | - vm.frame_count -= 1; |
2198 | | - |
2199 | | - // Check if caller is Lua function |
2200 | | - if unsafe { (*caller_ptr).is_lua() } { |
2201 | | - let result_reg = unsafe { (**frame_ptr_ptr).get_result_reg() }; |
2202 | | - |
2203 | | - // Update frame_ptr to caller |
2204 | | - *frame_ptr_ptr = caller_ptr; |
2205 | | - |
2206 | | - // Write return value directly to caller's register |
2207 | | - let caller_base = unsafe { (*caller_ptr).base_ptr } as usize; |
2208 | | - unsafe { |
2209 | | - *vm.register_stack |
2210 | | - .get_unchecked_mut(caller_base + result_reg) = return_value; |
2211 | | - } |
2212 | | - |
2213 | | - return Ok(()); |
2214 | | - } else { |
2215 | | - // C function caller |
2216 | | - *frame_ptr_ptr = caller_ptr; |
2217 | | - vm.return_values.clear(); |
2218 | | - vm.return_values.push(return_value); |
2219 | | - return Err(LuaError::Exit); |
2220 | | - } |
2221 | | - } |
2222 | | - |
2223 | | - // No caller - exit VM |
2224 | | - vm.frame_count -= 1; |
2225 | | - vm.return_values.clear(); |
2226 | | - vm.return_values.push(return_value); |
2227 | | - Err(LuaError::Exit) |
2228 | | -} |
0 commit comments