Skip to content

Commit 3fc292a

Browse files
committed
improve meta
1 parent 82519e4 commit 3fc292a

3 files changed

Lines changed: 90 additions & 69 deletions

File tree

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

Lines changed: 60 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@
2020

2121
use crate::{
2222
CallInfo, Instruction, LUA_MASKCALL, LUA_MASKCOUNT, LUA_MASKLINE, LUA_MASKRET, LuaResult,
23-
LuaState, LuaValue, OpCode,
23+
LuaState, LuaValue, OpCode, TablePtr,
2424
lua_value::LUA_VNUMINT,
2525
lua_vm::{
2626
LuaError, TmKind,
@@ -32,22 +32,50 @@ use crate::{
3232
helper::{
3333
bin_tm_fallback, eq_fallback, error_div_by_zero, error_global, error_mod_by_zero,
3434
finishget_fallback, finishset_fallback, finishset_fallback_known_miss,
35-
float_for_loop, fltvalue, forprep, handle_pending_ops, ivalue, lua_fmod, lua_idiv,
36-
lua_imod, lua_shiftl, lua_shiftr, luai_numpow, objlen, order_tm_fallback,
37-
pfltvalue, pivalue, psetfltvalue, psetivalue, ptonumberns, pttisfloat,
38-
pttisinteger, return0_with_hook, return1_with_hook, self_shortstr_index_chain_fast,
39-
setbfvalue, setbtvalue, setfltvalue, setivalue, setnilvalue, setobj2s, setobjs2s,
40-
tointeger, tointegerns, tonumberns, ttisfloat, ttisinteger, ttisstring,
41-
unary_tm_fallback,
35+
float_for_loop, fltvalue, forprep, get_metamethod_from_meta_ptr,
36+
handle_pending_ops, ivalue, lua_fmod, lua_idiv, lua_imod, lua_shiftl, lua_shiftr,
37+
luai_numpow, objlen, order_tm_fallback, pfltvalue, pivalue, psetfltvalue,
38+
psetivalue, ptonumberns, pttisfloat, pttisinteger, return0_with_hook,
39+
return1_with_hook, self_shortstr_index_chain_fast, setbfvalue, setbtvalue,
40+
setfltvalue, setivalue, setnilvalue, setobj2s, setobjs2s, tointeger, tointegerns,
41+
tonumberns, ttisfloat, ttisinteger, ttisstring, unary_tm_fallback,
4242
},
4343
hook::{hook_check_instruction, hook_on_call},
44+
metamethod::call_tm,
4445
number::{le_num, lt_num},
4546
vararg::{exec_varargprep, get_vararg, get_varargs},
4647
},
4748
lua_limits::EXTRA_STACK,
4849
},
4950
};
5051

52+
#[inline(always)]
53+
fn call_newindex_tm_fast(
54+
lua_state: &mut LuaState,
55+
ci: &mut CallInfo,
56+
obj: LuaValue,
57+
meta: TablePtr,
58+
key: LuaValue,
59+
value: LuaValue,
60+
) -> LuaResult<bool> {
61+
let Some(tm) = get_metamethod_from_meta_ptr(lua_state, meta, TmKind::NewIndex) else {
62+
return Ok(false);
63+
};
64+
if !tm.is_function() {
65+
return Ok(false);
66+
}
67+
68+
match call_tm(lua_state, tm, obj, key, value) {
69+
Ok(()) => Ok(true),
70+
Err(LuaError::Yield) => {
71+
ci.set_pending_finish_get(-2);
72+
ci.call_status |= CIST_PENDING_FINISH;
73+
Err(LuaError::Yield)
74+
}
75+
Err(e) => Err(e),
76+
}
77+
}
78+
5179
/// Execute until call depth reaches target_depth
5280
/// Used for protected calls (pcall) to execute only the called function
5381
/// without affecting caller frames
@@ -462,11 +490,12 @@ pub fn lua_execute(lua_state: &mut LuaState, target_depth: usize) -> LuaResult<(
462490
"GetTabUp key must be short string for fast path"
463491
);
464492
let mut known_newindex_miss = false;
493+
let mut meta = TablePtr::null();
465494
if upval_value.is_table() {
466495
let table = upval_value.hvalue_mut();
467496
let table_ptr = unsafe { upval_value.as_table_ptr_unchecked() };
468497
let gc_ptr = unsafe { upval_value.as_gc_ptr_table_unchecked() };
469-
let meta = table.meta_ptr();
498+
meta = table.meta_ptr();
470499
if meta.is_null() || meta.as_mut_ref().data.no_tm(TmKind::NewIndex.into()) {
471500
let (new_key, delta, rc_tt) = if instr.get_k() {
472501
let rc = *k_val!(c);
@@ -538,6 +567,10 @@ pub fn lua_execute(lua_state: &mut LuaState, target_depth: usize) -> LuaResult<(
538567
};
539568
savestate!();
540569
if known_newindex_miss {
570+
if call_newindex_tm_fast(lua_state, ci, upval_value, meta, *key, rc)? {
571+
updatetrap!();
572+
continue;
573+
}
541574
finishset_fallback_known_miss(lua_state, ci, &upval_value, key, rc)?;
542575
} else {
543576
finishset_fallback(lua_state, ci, &upval_value, key, rc)?;
@@ -635,6 +668,10 @@ pub fn lua_execute(lua_state: &mut LuaState, target_depth: usize) -> LuaResult<(
635668
*stack_val!(c)
636669
};
637670
savestate!();
671+
if call_newindex_tm_fast(lua_state, ci, ra, meta, rb, rc)? {
672+
updatetrap!();
673+
continue;
674+
}
638675
finishset_fallback_known_miss(lua_state, ci, &ra, &rb, rc)?;
639676
updatetrap!();
640677
continue;
@@ -702,6 +739,10 @@ pub fn lua_execute(lua_state: &mut LuaState, target_depth: usize) -> LuaResult<(
702739
continue;
703740
}
704741
savestate!();
742+
if call_newindex_tm_fast(lua_state, ci, ra, meta, rb, rc)? {
743+
updatetrap!();
744+
continue;
745+
}
705746
finishset_fallback_known_miss(lua_state, ci, &ra, &rb, rc)?;
706747
updatetrap!();
707748
continue;
@@ -781,6 +822,10 @@ pub fn lua_execute(lua_state: &mut LuaState, target_depth: usize) -> LuaResult<(
781822
let ra = *ra;
782823
let rb = LuaValue::integer(b);
783824
savestate!();
825+
if call_newindex_tm_fast(lua_state, ci, ra, meta, rb, rc)? {
826+
updatetrap!();
827+
continue;
828+
}
784829
finishset_fallback_known_miss(lua_state, ci, &ra, &rb, rc)?;
785830
updatetrap!();
786831
continue;
@@ -809,9 +854,10 @@ pub fn lua_execute(lua_state: &mut LuaState, target_depth: usize) -> LuaResult<(
809854
"SetField key must be short string for fast path"
810855
);
811856
let mut known_newindex_miss = false;
857+
let mut meta = TablePtr::null();
812858
if unsafe { (*ra_ptr).is_table() } {
813859
let table = unsafe { (*ra_ptr).hvalue_mut() };
814-
let meta = table.meta_ptr();
860+
meta = table.meta_ptr();
815861
if meta.is_null() || meta.as_mut_ref().data.no_tm(TmKind::NewIndex.into()) {
816862
let (new_key, delta, rc_tt) = if instr.get_k() {
817863
let rc = *k_val!(c);
@@ -894,6 +940,10 @@ pub fn lua_execute(lua_state: &mut LuaState, target_depth: usize) -> LuaResult<(
894940
let rb = *key;
895941
savestate!();
896942
if known_newindex_miss {
943+
if call_newindex_tm_fast(lua_state, ci, ra, meta, rb, rc)? {
944+
updatetrap!();
945+
continue;
946+
}
897947
finishset_fallback_known_miss(lua_state, ci, &ra, &rb, rc)?;
898948
} else {
899949
finishset_fallback(lua_state, ci, &ra, &rb, rc)?;

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

Lines changed: 29 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
use crate::{
2-
CallInfo, Chunk, LuaResult, LuaValue,
2+
CallInfo, Chunk, LuaResult, LuaValue, TablePtr,
33
lua_value::{LUA_VNUMFLT, LUA_VNUMINT, udvalue_to_lua_value},
44
lua_vm::{
55
LuaError, LuaState, TmKind,
@@ -440,8 +440,6 @@ fn finishget_inner(
440440
key: &LuaValue,
441441
skip_first_raw_lookup: bool,
442442
) -> LuaResult<Option<LuaValue>> {
443-
const TM_INDEX_BIT: u8 = TmKind::Index as u8; // = 0
444-
445443
let mut t = *obj;
446444
let mut skip_raw_lookup = skip_first_raw_lookup;
447445

@@ -458,24 +456,9 @@ fn finishget_inner(
458456
skip_raw_lookup = false;
459457
}
460458

461-
let meta = table.meta_ptr();
462-
if meta.is_null() {
463-
return Ok(None); // No metatable → no __index
464-
}
465-
let mt = unsafe { &mut (*meta.as_mut_ptr()).data };
466-
// fasttm: check cached bit
467-
if mt.no_tm(TM_INDEX_BIT) {
468-
return Ok(None); // __index known absent
469-
}
470-
// Slow path: hash lookup (use get_shortstr_fast — TM keys are always interned strings)
471-
let vm = lua_state.vm_mut();
472-
let event_key = vm.const_strings.get_tm_value(TmKind::Index);
473-
match mt.impl_table.get_shortstr_fast(&event_key) {
459+
match get_metamethod_from_meta_ptr(lua_state, table.meta_ptr(), TmKind::Index) {
474460
Some(v) => v,
475-
None => {
476-
mt.set_tm_absent(TM_INDEX_BIT); // Cache absence
477-
return Ok(None);
478-
}
461+
None => return Ok(None),
479462
}
480463
} else {
481464
// Non-table (string, userdata): check trait-based field access first
@@ -551,29 +534,36 @@ fn get_metamethod_from_metatable(
551534
metatable: LuaValue,
552535
tm_kind: TmKind,
553536
) -> Option<LuaValue> {
554-
if let Some(mt) = metatable.as_table_mut() {
555-
let tm_idx = tm_kind as u8;
537+
metatable
538+
.as_table_ptr()
539+
.and_then(|meta_ptr| get_metamethod_from_meta_ptr(lua_state, meta_ptr, tm_kind))
540+
}
556541

557-
// fasttm: check bit-flag cache — now covers all 26 TmKind values
558-
if mt.no_tm(tm_idx) {
559-
return None; // Known absent — skip hash lookup entirely
560-
}
542+
#[inline]
543+
pub(crate) fn get_metamethod_from_meta_ptr(
544+
lua_state: &mut LuaState,
545+
meta_ptr: TablePtr,
546+
tm_kind: TmKind,
547+
) -> Option<LuaValue> {
548+
if meta_ptr.is_null() {
549+
return None;
550+
}
561551

562-
let vm = lua_state.vm_mut();
563-
let event_key = vm.const_strings.get_tm_value(tm_kind);
564-
// TM keys are always interned short strings — use get_shortstr_fast
565-
// to skip float normalization and integer check in raw_get
566-
let result = mt.impl_table.get_shortstr_fast(&event_key);
552+
let mt = unsafe { &mut (*meta_ptr.as_mut_ptr()).data };
553+
let tm_idx = tm_kind as u8;
554+
if mt.no_tm(tm_idx) {
555+
return None;
556+
}
567557

568-
if result.is_none() {
569-
// Cache that this TM is absent (luaT_gettm pattern)
570-
mt.set_tm_absent(tm_idx);
571-
}
558+
let vm = lua_state.vm_mut();
559+
let event_key = vm.const_strings.get_tm_value(tm_kind);
560+
let result = mt.impl_table.get_shortstr_fast(&event_key);
572561

573-
return result;
562+
if result.is_none() {
563+
mt.set_tm_absent(tm_idx);
574564
}
575565

576-
None
566+
result
577567
}
578568

579569
/// Port of Lua 5.5's luaV_finishset from lvm.c:334
@@ -636,33 +626,14 @@ fn finishset_inner(
636626
return Err(lua_state.error("table index is NaN".to_string()));
637627
}
638628

639-
const TM_NEWINDEX_BIT: u8 = TmKind::NewIndex as u8; // = 1
640-
641629
let mut t = *obj;
642630
let mut skip_existing = skip_existing_check;
643631

644632
for _ in 0..MAXTAGLOOP {
645633
// Check if t is a table — use inline fasttm for __newindex
646634
if let Some(table) = t.as_table_mut() {
647-
let meta = table.meta_ptr();
648-
let tm_val = if meta.is_null() {
649-
None
650-
} else {
651-
let mt = unsafe { &mut (*meta.as_mut_ptr()).data };
652-
// fasttm: check cached bit for __newindex
653-
if mt.no_tm(TM_NEWINDEX_BIT) {
654-
None
655-
} else {
656-
let vm = lua_state.vm_mut();
657-
let event_key = vm.const_strings.get_tm_value(TmKind::NewIndex);
658-
// TM keys are always interned short strings
659-
let result = mt.impl_table.get_shortstr_fast(&event_key);
660-
if result.is_none() {
661-
mt.set_tm_absent(TM_NEWINDEX_BIT);
662-
}
663-
result
664-
}
665-
};
635+
let tm_val =
636+
get_metamethod_from_meta_ptr(lua_state, table.meta_ptr(), TmKind::NewIndex);
666637

667638
if tm_val.is_none() {
668639
// No metamethod - set directly

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,5 +11,5 @@ mod vararg;
1111
pub use execute_loop::lua_execute;
1212
pub use helper::{get_metamethod_event, get_metatable};
1313
pub use metamethod::TmKind;
14-
pub use metamethod::call_tm_res1;
1514
pub use metamethod::call_tm_res;
15+
pub use metamethod::call_tm_res1;

0 commit comments

Comments
 (0)