Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 8 additions & 8 deletions crates/luars/src/compiler/expr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1609,21 +1609,21 @@ fn compile_binary_expr_to(
BinaryOperator::OpConcat => {
// CONCAT has special instruction format: CONCAT A B
// Concatenates R[A] through R[A+B] (B+1 values), result in R[A]

// LUA 5.4 OPTIMIZATION: Merge consecutive CONCAT operations
// When compiling "e1 .. e2", if e2's code just generated a CONCAT instruction,
// we can merge them into a single CONCAT that concatenates all values at once.
// This is critical for performance with chains like "a" .. "b" .. "c"

let code = &c.chunk.code;
if !code.is_empty() {
let prev_instr = code[code.len() - 1];
let prev_opcode = Instruction::get_opcode(prev_instr);

if prev_opcode == OpCode::Concat {
let prev_a = Instruction::get_a(prev_instr);
let prev_b = Instruction::get_b(prev_instr);

// Check if right_reg is the result of previous CONCAT
// Previous CONCAT: R[prev_a] = R[prev_a]..R[prev_a+1]..R[prev_a+prev_b]
// If right_reg == prev_a and left_reg == prev_a - 1, we can merge
Expand All @@ -1633,11 +1633,11 @@ fn compile_binary_expr_to(
let last_idx = code.len() - 1;
c.chunk.code[last_idx] = Instruction::encode_abc(
OpCode::Concat,
left_reg, // Start from left_reg instead
prev_b + 1, // Increase count by 1
0
left_reg, // Start from left_reg instead
prev_b + 1, // Increase count by 1
0,
);

// BUGFIX: Respect dest parameter
if let Some(d) = dest {
if d != left_reg {
Expand Down
2 changes: 1 addition & 1 deletion crates/luars/src/gc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -139,7 +139,7 @@ impl GC {
allocations_since_minor_gc: 0,
minor_gc_count: 0,
check_counter: 0,
check_interval: 1000, // Run GC every 1000 checks when debt > 0 (reduce overhead)
check_interval: 10000, // Run GC every 10000 checks when debt > 0 (massive overhead reduction)
finalize_queue: Vec::new(), // __gc finalizer queue
collection_count: 0,
stats: GCStats::default(),
Expand Down
41 changes: 14 additions & 27 deletions crates/luars/src/lib_registry.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
// Provides a clean way to register Rust functions as Lua libraries

use crate::lua_value::{CFunction, LuaValue};
use crate::lua_vm::{LuaError, LuaResult, LuaVM};
use crate::lua_vm::{LuaResult, LuaVM};
use crate::stdlib;

/// Type for value initializers - functions that create values when the module loads
Expand Down Expand Up @@ -189,7 +189,7 @@ pub fn create_standard_registry() -> LibraryRegistry {
registry.register(stdlib::string::create_string_lib());
registry.register(stdlib::table::create_table_lib());
registry.register(stdlib::math::create_math_lib());
registry.register(stdlib::io::create_io_lib());
// registry.register(stdlib::io::create_io_lib());
registry.register(stdlib::os::create_os_lib());
registry.register(stdlib::utf8::create_utf8_lib());
registry.register(stdlib::coroutine::create_coroutine_lib());
Expand All @@ -213,17 +213,18 @@ pub fn get_args(vm: &LuaVM) -> Vec<LuaValue> {
}

/// Helper to get a specific argument
/// 1 based index
#[inline(always)]
pub fn get_arg(vm: &LuaVM, index: usize) -> Option<LuaValue> {
let frame = vm.frames.last().unwrap();
let base_ptr = frame.base_ptr;
let top = frame.top;

// Arguments are 0-indexed from caller's perspective
// But register 0 is the function, so arg 0 is at register 1
// get_arg(0) should return register[base_ptr + 1]
// get_arg(1) should return register[base_ptr + 2]
let reg_offset = index + 1; // +1 to skip the function at register 0
// Arguments use 1-based indexing (Lua convention)
// Register 0 is the function itself
// get_arg(1) returns register[base_ptr + 1] (first argument)
// get_arg(2) returns register[base_ptr + 2] (second argument)
let reg_offset = index;
if reg_offset < top {
let reg_index = base_ptr + reg_offset;
if reg_index < vm.register_stack.len() {
Expand All @@ -237,11 +238,13 @@ pub fn get_arg(vm: &LuaVM, index: usize) -> Option<LuaValue> {
}

/// Helper to require an argument
/// 1 based index
#[inline]
pub fn require_arg(vm: &LuaVM, index: usize, func_name: &str) -> LuaResult<LuaValue> {
get_arg(vm, index).ok_or_else(|| {
LuaError::RuntimeError(format!("{}() requires argument {}", func_name, index + 1))
})
pub fn require_arg(vm: &mut LuaVM, index: usize, func_name: &str) -> LuaResult<LuaValue> {
let Some(arg) = get_arg(vm, index) else {
return Err(vm.error(format!("{}() requires argument {}", func_name, index + 1)));
};
Ok(arg)
}

/// Helper to get argument count
Expand All @@ -251,19 +254,3 @@ pub fn arg_count(vm: &LuaVM) -> usize {
// Subtract 1 for the function itself
frame.top.saturating_sub(1)
}

/// Helper to get string argument
pub fn get_string(vm: &LuaVM, index: usize, func_name: &str) -> LuaResult<String> {
let arg = require_arg(vm, index, func_name)?;
unsafe {
arg.as_string()
.map(|s| s.as_str().to_string())
.ok_or_else(|| {
LuaError::RuntimeError(format!(
"{}() requires string argument {}",
func_name,
index + 1
))
})
}
}
28 changes: 11 additions & 17 deletions crates/luars/src/lua_value/lua_table.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,8 @@
// High-performance Lua table implementation following Lua 5.4 design
// - Array part for integer keys [1..n]
// - Hash part using open addressing (same as Lua 5.4)
use crate::LuaVM;
use crate::lua_vm::{LuaError, LuaResult};

use super::LuaValue;
use crate::LuaVM;

/// Hash node - mimics Lua 5.4's Node structure
/// Contains key+value pair and next index for collision chaining
Expand Down Expand Up @@ -61,7 +59,7 @@ impl LuaTable {
} else {
0
};

LuaTable {
array: if array_size > 0 {
Some(Vec::with_capacity(array_size))
Expand Down Expand Up @@ -162,7 +160,7 @@ impl LuaTable {
if self.nodes.is_none() {
panic!("insert_node_simple called with uninitialized nodes - this is a bug");
}

let nodes = self.nodes.as_mut().unwrap();
let size = nodes.len();
let mut idx = Self::hash_key(&key, size);
Expand Down Expand Up @@ -291,15 +289,15 @@ impl LuaTable {

if key > 0 {
let idx = (key - 1) as usize;

// Initialize array if needed
if self.array.is_none() {
self.array = Some(Vec::new());
}

let array = self.array.as_mut().unwrap();
let array_len = array.len();

// Limit array growth to reasonable size (64K elements)
if idx < array_len {
array[idx] = value;
Expand Down Expand Up @@ -410,19 +408,17 @@ impl LuaTable {

/// Insert value at position in array part, shifting elements to the right
/// Position is 0-indexed internally but Lua uses 1-indexed
pub fn insert_array_at(&mut self, pos: usize, value: LuaValue) -> LuaResult<()> {
pub fn insert_array_at(&mut self, pos: usize, value: LuaValue) -> Result<(), String> {
let len = self.len();
if pos > len {
return Err(LuaError::RuntimeError(
"insert position out of bounds".to_string(),
));
return Err("insert position out of bounds".to_string());
}

// Initialize array if needed
if self.array.is_none() {
self.array = Some(Vec::new());
}

let array = self.array.as_mut().unwrap();

// CRITICAL OPTIMIZATION: Fast path for appending at end (no shift needed!)
Expand All @@ -439,12 +435,10 @@ impl LuaTable {

/// Remove value at position in array part, shifting elements to the left
/// Position is 0-indexed internally but Lua uses 1-indexed
pub fn remove_array_at(&mut self, pos: usize) -> LuaResult<LuaValue> {
pub fn remove_array_at(&mut self, pos: usize) -> Result<LuaValue, String> {
let len = self.len();
if pos >= len {
return Err(LuaError::RuntimeError(
"remove position out of bounds".to_string(),
));
return Err("remove position out of bounds".to_string());
}

let array = self.array.as_mut().expect("array should exist");
Expand Down
4 changes: 2 additions & 2 deletions crates/luars/src/lua_value/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,8 @@ pub use lua_table::LuaTable;
pub use lua_thread::*;
pub use lua_value::{
LuaValue, LuaValueKind, NAN_BASE, TAG_BOOLEAN, TAG_CFUNCTION, TAG_FLOAT, TAG_FUNCTION,
TAG_INTEGER, TAG_NIL, TAG_STRING, TAG_TABLE, TAG_USERDATA, TYPE_MASK,
VALUE_TRUE, VALUE_FALSE, VALUE_NIL,
TAG_INTEGER, TAG_NIL, TAG_STRING, TAG_TABLE, TAG_USERDATA, TYPE_MASK, VALUE_FALSE, VALUE_NIL,
VALUE_TRUE,
};

/// Multi-return values from Lua functions
Expand Down
Loading