mirror of
https://github.com/pjh456/OxideJS.git
synced 2026-10-23 18:21:13 +08:00
perf: 数组 length 读走 IC 虚拟槽快路径
This commit is contained in:
@@ -5,7 +5,7 @@ use oxide_types::object::JsObject;
|
||||
use oxide_types::private_key::{int_key_value, is_int_key, make_int_key, make_private_name_id};
|
||||
use oxide_types::value::JsValue;
|
||||
|
||||
use crate::ic_helper::{self, ic_get_hit, ic_set_hit};
|
||||
use crate::ic_helper::{self, ic_get_hit, ic_set_hit, VIRTUAL_LENGTH_SLOT};
|
||||
use crate::vm::{Vm, MAX_PROTO_CHAIN_DEPTH};
|
||||
|
||||
#[cold]
|
||||
@@ -377,6 +377,14 @@ impl Vm {
|
||||
let prop_name_si = self.property_key_si(self.regs[b])?;
|
||||
self.profiling.record_ic_miss();
|
||||
prop_cache_miss();
|
||||
// 数组 length 虚拟槽:Array.prototype 无 own length,写回恒落空;
|
||||
// 直接学入标记并读逻辑长度,跳过模板查 / ordinary_get / proto 空跑。
|
||||
if obj.is_array() && prop_name_si == self.length_si {
|
||||
ic_helper::write_ic_back(self.bytecode_mut(), ic_pc, obj.shape_id(), VIRTUAL_LENGTH_SLOT, 0);
|
||||
ic_debug!("IC_GET virtual length hit shape={}", obj.shape_id());
|
||||
self.regs[a] = obj.logical_len_value();
|
||||
return Ok(());
|
||||
}
|
||||
// 模板快路径:prop_forge 只缓存每 shape 最后新增的属性,恰好覆盖该属性时直取槽位。
|
||||
if let Some(template) = self.kernel_core.prop_forge().get_template(obj.shape_id()) {
|
||||
if template.prop_name == prop_name_si && template.position < obj.prop_vec_len() as u32 {
|
||||
|
||||
@@ -12,12 +12,19 @@
|
||||
//! - miss 写回 FIFO 滚动:新条目进槽 0、原槽 0..2 顺移到槽 1..3、槽 3 丢弃,
|
||||
//! 空槽恒在尾部连续——命中遍历遇空槽即可 break。
|
||||
//! - 命中路径零写:只读扩展字 + Cell 计数,不触发 bytecode COW。
|
||||
//! - `slot_index == [`VIRTUAL_LENGTH_SLOT`]` 为数组虚拟长度标记,不对应任何实际槽,
|
||||
//! 命中判定必须带 `is_array()` 守卫(`EMPTY_SHAPE_ID` 跨对象类型共享)。
|
||||
|
||||
use crate::vm_trace;
|
||||
use oxide_bytecode::opcode::{self, ext_word_count, Instr, IC_EXT_WORDS, IC_SLOTS};
|
||||
use oxide_types::object::JsObject;
|
||||
use oxide_types::value::JsValue;
|
||||
|
||||
/// 数组虚拟长度标记槽:与真实槽空间不相交(`prop_vec_len` 远小于 `u32::MAX`),
|
||||
/// 供 `arr.length` 读快路径使用——Array.prototype 无 own length 槽,写回恒落空,
|
||||
/// miss 时直接学入本标记,命中时读逻辑长度。
|
||||
pub(crate) const VIRTUAL_LENGTH_SLOT: u32 = u32::MAX;
|
||||
|
||||
/// 读取 `pc` 处 IC 指令槽 0 的三元组并把 `pc` 推进越过全部扩展字(单态快路径用)。
|
||||
/// 槽编码:[shape_id(24)|proto_depth(8)] [slot(32)]。
|
||||
pub(crate) fn read_ic_slot0(bytecode: &[Instr], pc: &mut usize) -> (u32, u32, u8) {
|
||||
@@ -96,6 +103,11 @@ pub(crate) fn ic_get_hit(obj: &JsObject, shape_id: u32, slot_index: u32, proto_d
|
||||
return None;
|
||||
}
|
||||
if proto_depth == 0 {
|
||||
// 虚拟长度标记:EMPTY_SHAPE_ID 跨对象类型共享,守卫必须带 is_array(),
|
||||
// 防无属性普通对象(shape 1)误命中返回 0。
|
||||
if obj.is_array() && obj.shape_id() == shape_id && slot_index == VIRTUAL_LENGTH_SLOT {
|
||||
return Some(obj.logical_len_value());
|
||||
}
|
||||
if obj.shape_id() == shape_id && slot_index < obj.prop_vec_len() as u32 {
|
||||
let v = obj.get_prop_shape(slot_index);
|
||||
return Some(v);
|
||||
@@ -127,6 +139,9 @@ pub(crate) fn ic_get_hit_poly(obj: &JsObject, bytecode: &[Instr], ext_pc: usize)
|
||||
let slot_index = bytecode[base + 1];
|
||||
let proto_depth = (bytecode[base] >> 24) as u8;
|
||||
if proto_depth == 0 {
|
||||
if obj.is_array() && obj.shape_id() == shape_id && slot_index == VIRTUAL_LENGTH_SLOT {
|
||||
return Some(obj.logical_len_value());
|
||||
}
|
||||
if obj.shape_id() == shape_id && slot_index < obj.prop_vec_len() as u32 {
|
||||
let v = obj.get_prop_shape(slot_index);
|
||||
return Some(v);
|
||||
@@ -254,6 +269,7 @@ fn ic_set_hit_own_poly(obj: &mut JsObject, bytecode: &[Instr], ext_pc: usize, va
|
||||
mod tests {
|
||||
use super::*;
|
||||
use oxide_bytecode::opcode::OpCode;
|
||||
use oxide_types::shape::EMPTY_SHAPE_ID;
|
||||
|
||||
#[test]
|
||||
fn concat_n_ext_word_count_advances_by_n() {
|
||||
@@ -367,4 +383,19 @@ mod tests {
|
||||
assert_eq!(bc[6], 0xA3);
|
||||
assert_eq!(bc[7], 3);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn ic_get_hit_virtual_length_marker_hits_array() {
|
||||
// shape 1(空形状)数组经虚拟长度标记命中:返回逻辑长度。
|
||||
let bump = bumpalo::Bump::new();
|
||||
let arr = JsObject::new_array(EMPTY_SHAPE_ID, JsValue::null(), 3, &bump);
|
||||
assert_eq!(ic_get_hit(&arr, EMPTY_SHAPE_ID, VIRTUAL_LENGTH_SLOT, 0), Some(JsValue::int(3)));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn ic_get_hit_virtual_length_marker_rejects_non_array() {
|
||||
// 同 shape 1 的普通对象(EMPTY_SHAPE_ID 跨对象类型共享)不得误命中虚拟长度标记。
|
||||
let plain = JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::null());
|
||||
assert_eq!(ic_get_hit(&plain, EMPTY_SHAPE_ID, VIRTUAL_LENGTH_SLOT, 0), None);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user