mirror of
https://github.com/pjh456/OxideJS.git
synced 2026-10-24 02:31:13 +08:00
perf: member 复合写路径接入 IC 写命中(depth 0)消除每次 DashMap 查询与回写
set_member_prop 增加 ic_set_hit_own(仅接受 depth==0 槽,跳过读侧学习的 原型条目防原型污染)命中直写槽,ext_pc 由 read_member_prop 显式带出消除 隐式 pc 契约;写新属性 miss 分支(IC_SET 分发 + member 写)统一改 CreateDataProperty 快路径(保留原型链 setter/readonly 检查),砍掉 ordinary_set 全链路的重复查询与 shape 转换开销。
This commit is contained in:
@@ -126,10 +126,10 @@ impl Vm {
|
||||
};
|
||||
let receiver = self.regs[rd];
|
||||
let obj = unsafe { &mut *obj_ptr };
|
||||
let prop_val = self.read_member_prop(obj, prop_name_si, receiver)?;
|
||||
let (prop_val, ext_pc) = self.read_member_prop(obj, prop_name_si, receiver)?;
|
||||
let n = self.coerce_number_bounded(prop_val)?;
|
||||
let new_val = JsValue::float(n + 1.0);
|
||||
self.set_member_prop(obj, prop_name_si, new_val, receiver)?;
|
||||
self.set_member_prop(obj, prop_name_si, new_val, receiver, ext_pc)?;
|
||||
self.regs[a] = new_val;
|
||||
Ok(())
|
||||
}
|
||||
@@ -141,10 +141,10 @@ impl Vm {
|
||||
};
|
||||
let receiver = self.regs[rd];
|
||||
let obj = unsafe { &mut *obj_ptr };
|
||||
let prop_val = self.read_member_prop(obj, prop_name_si, receiver)?;
|
||||
let (prop_val, ext_pc) = self.read_member_prop(obj, prop_name_si, receiver)?;
|
||||
let n = self.coerce_number_bounded(prop_val)?;
|
||||
let new_val = JsValue::float(n - 1.0);
|
||||
self.set_member_prop(obj, prop_name_si, new_val, receiver)?;
|
||||
self.set_member_prop(obj, prop_name_si, new_val, receiver, ext_pc)?;
|
||||
self.regs[a] = new_val;
|
||||
Ok(())
|
||||
}
|
||||
@@ -187,7 +187,7 @@ impl Vm {
|
||||
return Ok(());
|
||||
};
|
||||
let receiver = self.regs[rd];
|
||||
let prop_raw = {
|
||||
let (prop_raw, ext_pc) = {
|
||||
let obj = unsafe { &mut *obj_ptr };
|
||||
self.read_member_prop(obj, prop_name_si, receiver)?
|
||||
};
|
||||
@@ -203,7 +203,7 @@ impl Vm {
|
||||
JsValue::float(ln + rn)
|
||||
};
|
||||
let obj = unsafe { &mut *obj_ptr };
|
||||
self.set_member_prop(obj, prop_name_si, new_val, receiver)?;
|
||||
self.set_member_prop(obj, prop_name_si, new_val, receiver, ext_pc)?;
|
||||
self.regs[a] = new_val;
|
||||
Ok(())
|
||||
}
|
||||
@@ -219,16 +219,16 @@ impl Vm {
|
||||
return Ok(());
|
||||
};
|
||||
let receiver = self.regs[rd];
|
||||
let ln = {
|
||||
let (ln, ext_pc) = {
|
||||
let obj = unsafe { &mut *obj_ptr };
|
||||
let prop_val = self.read_member_prop(obj, prop_name_si, receiver)?;
|
||||
self.coerce_number_bounded(prop_val)?
|
||||
let (prop_val, ext_pc) = self.read_member_prop(obj, prop_name_si, receiver)?;
|
||||
(self.coerce_number_bounded(prop_val)?, ext_pc)
|
||||
};
|
||||
let rn = self.coerce_number_bounded(self.regs[a])?;
|
||||
let new_val = JsValue::float(op(ln, rn));
|
||||
self.regs[a] = new_val;
|
||||
let obj = unsafe { &mut *obj_ptr };
|
||||
self.set_member_prop(obj, prop_name_si, new_val, receiver)?;
|
||||
self.set_member_prop(obj, prop_name_si, new_val, receiver, ext_pc)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -243,16 +243,16 @@ impl Vm {
|
||||
return Ok(());
|
||||
};
|
||||
let receiver = self.regs[rd];
|
||||
let ln = {
|
||||
let (ln, ext_pc) = {
|
||||
let obj = unsafe { &mut *obj_ptr };
|
||||
let prop_val = self.read_member_prop(obj, prop_name_si, receiver)?;
|
||||
self.coerce_int32_bounded(prop_val)?
|
||||
let (prop_val, ext_pc) = self.read_member_prop(obj, prop_name_si, receiver)?;
|
||||
(self.coerce_int32_bounded(prop_val)?, ext_pc)
|
||||
};
|
||||
let rn = self.coerce_int32_bounded(self.regs[a])?;
|
||||
let new_val = JsValue::int(op(ln, rn));
|
||||
self.regs[a] = new_val;
|
||||
let obj = unsafe { &mut *obj_ptr };
|
||||
self.set_member_prop(obj, prop_name_si, new_val, receiver)?;
|
||||
self.set_member_prop(obj, prop_name_si, new_val, receiver, ext_pc)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -265,10 +265,10 @@ impl Vm {
|
||||
return Ok(());
|
||||
};
|
||||
let receiver = self.regs[rd];
|
||||
let ln = {
|
||||
let (ln, ext_pc) = {
|
||||
let obj = unsafe { &mut *obj_ptr };
|
||||
let prop_val = self.read_member_prop(obj, prop_name_si, receiver)?;
|
||||
self.coerce_int32_bounded(prop_val)?
|
||||
let (prop_val, ext_pc) = self.read_member_prop(obj, prop_name_si, receiver)?;
|
||||
(self.coerce_int32_bounded(prop_val)?, ext_pc)
|
||||
};
|
||||
let shift = self.coerce_uint32_bounded(self.regs[a])? & 0x1F;
|
||||
let new_val = if !is_right {
|
||||
@@ -285,7 +285,7 @@ impl Vm {
|
||||
};
|
||||
self.regs[a] = new_val;
|
||||
let obj = unsafe { &mut *obj_ptr };
|
||||
self.set_member_prop(obj, prop_name_si, new_val, receiver)?;
|
||||
self.set_member_prop(obj, prop_name_si, new_val, receiver, ext_pc)?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
use crate::{ic_debug, ic_trace, vm_trace};
|
||||
use oxide_bytecode::opcode::{OpCode, IC_EXT_WORDS};
|
||||
use oxide_kernel::prop_forge::PropTemplate;
|
||||
use oxide_runtime_api as coercion;
|
||||
use oxide_types::object::JsObject;
|
||||
use oxide_types::private_key::{int_key_value, is_int_key, make_private_name_id};
|
||||
@@ -443,23 +442,14 @@ impl Vm {
|
||||
ic_debug!("IC_SET write-back shape={} slot={}", obj.shape_id(), pos);
|
||||
} else {
|
||||
self.profiling.record_ic_miss();
|
||||
let old_shape = obj.shape_id();
|
||||
self.ordinary_set_dispatch(obj, prop_name_si, value, receiver)?;
|
||||
if old_shape != obj.shape_id() {
|
||||
if let Some(pos) = self.kernel_core.shape_forge().lookup_position(obj.shape_id(), prop_name_si) {
|
||||
ic_helper::write_ic_back(self.bytecode_mut(), ic_pc, obj.shape_id(), pos, 0);
|
||||
ic_debug!("IC_SET write-back shape={} slot={}", obj.shape_id(), pos);
|
||||
self.kernel_core.prop_forge().upsert(
|
||||
obj.shape_id(),
|
||||
PropTemplate {
|
||||
shape_id: obj.shape_id(),
|
||||
prop_name: prop_name_si,
|
||||
position: pos,
|
||||
generation: obj.generation(),
|
||||
},
|
||||
);
|
||||
}
|
||||
prop_cache_miss();
|
||||
// 写新属性(shape 转换):数组 length / 整数索引键仍走完整
|
||||
// ordinary_set_dispatch 语义,其余走 CreateDataProperty 快路径。
|
||||
if self.named_prop_create_needs_ordinary_set(obj, prop_name_si) {
|
||||
self.ordinary_set_dispatch(obj, prop_name_si, value, receiver)?;
|
||||
return Ok(());
|
||||
}
|
||||
self.create_named_prop_fast(obj, prop_name_si, value, receiver, ic_pc, true)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
|
||||
@@ -239,3 +239,51 @@ pub(crate) fn ic_set_hit_poly(obj: &mut JsObject, bytecode: &[Instr], ext_pc: us
|
||||
}
|
||||
false
|
||||
}
|
||||
|
||||
/// member 复合写(读改写共用同一扩展字组)的写侧命中判定:顺序遍历 4 槽,只接受
|
||||
/// depth==0 的数据槽并直写。
|
||||
///
|
||||
/// 读侧会把继承属性解析结果以 depth>0 条目写回同一扩展字组;写侧若命中这些条目
|
||||
/// 会把新值直写原型对象(原型污染,规范要求 shadow 到接收者 own 属性)——因此
|
||||
/// 原型条目一律跳过,落到慢路径走完整 [[Set]] 语义。
|
||||
///
|
||||
/// `pc` 为越过该 IC 指令全部扩展字后的位置(与 [`write_ic_back`] 同一约定),
|
||||
/// 命中遍历从 `pc - IC_EXT_WORDS` 起的扩展字开始。
|
||||
#[inline(always)]
|
||||
pub(crate) fn ic_set_hit_own(obj: &mut JsObject, bytecode: &[Instr], pc: usize, value: JsValue) -> bool {
|
||||
let base = pc - IC_EXT_WORDS;
|
||||
let shape_id = bytecode[base] & 0x00FF_FFFF;
|
||||
if shape_id == 0 {
|
||||
return false;
|
||||
}
|
||||
let proto_depth = (bytecode[base] >> 24) as u8;
|
||||
let slot_index = bytecode[base + 1];
|
||||
if proto_depth == 0 && obj.shape_id() == shape_id && slot_index < obj.prop_vec_len() as u32 {
|
||||
obj.set_prop_shape(slot_index, value);
|
||||
return true;
|
||||
}
|
||||
ic_set_hit_own_poly(obj, bytecode, base, value)
|
||||
}
|
||||
|
||||
/// 写侧多态槽(1..3)遍历,depth==0 only;`inline(never)` 防主循环代码膨胀。
|
||||
/// `ext_pc` 为扩展字起始位置(首个扩展字处)。
|
||||
#[inline(never)]
|
||||
fn ic_set_hit_own_poly(obj: &mut JsObject, bytecode: &[Instr], ext_pc: usize, value: JsValue) -> bool {
|
||||
for i in 1..IC_SLOTS {
|
||||
let base = ext_pc + i * 2;
|
||||
let shape_id = bytecode[base] & 0x00FF_FFFF;
|
||||
if shape_id == 0 {
|
||||
break;
|
||||
}
|
||||
let proto_depth = (bytecode[base] >> 24) as u8;
|
||||
if proto_depth != 0 {
|
||||
continue;
|
||||
}
|
||||
let slot_index = bytecode[base + 1];
|
||||
if obj.shape_id() == shape_id && slot_index < obj.prop_vec_len() as u32 {
|
||||
obj.set_prop_shape(slot_index, value);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
false
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
use crate::vm::{FrameContinuation, Vm, MAX_PROTO_CHAIN_DEPTH};
|
||||
use crate::{ic_trace, vm_trace};
|
||||
use oxide_kernel::prop_forge::PropTemplate;
|
||||
use oxide_runtime_api as coercion;
|
||||
use oxide_types::object::{JsObject, PropAttributes, PropMetaEntry};
|
||||
use oxide_types::value::JsValue;
|
||||
@@ -333,15 +334,19 @@ impl Vm {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// 读取 member 复合操作数(obj.x / obj["x"]),返回 (值, IC 扩展字起始位置)。
|
||||
///
|
||||
/// `ext_pc` 供同一指令的写侧 [`Self::set_member_prop`] 使用:读侧负责推进 pc
|
||||
/// 越过扩展字,写侧显式接收该位置、不再接触 pc,消除隐式 pc 契约。
|
||||
pub(crate) fn read_member_prop(
|
||||
&mut self, obj: &JsObject, prop_name_si: u32, receiver: JsValue,
|
||||
) -> Result<JsValue, String> {
|
||||
) -> Result<(JsValue, usize), String> {
|
||||
vm_trace!("read_member_prop: shape_id={} prop_name_si={}", obj.shape_id(), prop_name_si);
|
||||
let (cached_shape_id, cached_slot, cached_depth) =
|
||||
crate::ic_helper::read_ic_slot0(&self.bytecode, &mut self.pc);
|
||||
let ic_pc = self.pc;
|
||||
if obj.has_prop_meta() {
|
||||
return self.ordinary_get(obj, prop_name_si, receiver);
|
||||
return self.ordinary_get(obj, prop_name_si, receiver).map(|v| (v, ic_pc));
|
||||
}
|
||||
|
||||
let val = if let Some(v) = crate::ic_helper::ic_get_hit(obj, cached_shape_id, cached_slot, cached_depth) {
|
||||
@@ -362,7 +367,7 @@ impl Vm {
|
||||
} else {
|
||||
self.proto_chain_ic_get(obj, prop_name_si, receiver)?
|
||||
};
|
||||
Ok(val)
|
||||
Ok((val, ic_pc))
|
||||
}
|
||||
|
||||
/// 执行 ordinary_get 并把 IC 按原型链深度写回(own 属性 depth=0,原型链按层计数)。
|
||||
@@ -398,25 +403,86 @@ impl Vm {
|
||||
Ok(resolved)
|
||||
}
|
||||
|
||||
/// member 复合写(obj.x += 1 / obj.x++ 等)的写侧:优先 IC 写命中(depth==0 槽)
|
||||
/// 直写返回,miss 走查表/写新属性快路径。
|
||||
///
|
||||
/// `ext_pc` 由调用方从 [`Self::read_member_prop`] 返回值带出(读侧已把 pc 推进
|
||||
/// 越过本 IC 指令扩展字后的位置);写侧不再接触 pc,避免二次推进导致指令定位错位。
|
||||
pub(crate) fn set_member_prop(
|
||||
&mut self, obj: &mut JsObject, prop_name_si: u32, val: JsValue, receiver: JsValue,
|
||||
&mut self, obj: &mut JsObject, prop_name_si: u32, val: JsValue, receiver: JsValue, ext_pc: usize,
|
||||
) -> Result<(), String> {
|
||||
ic_trace!("set_member_prop: shape_id={} prop_name_si={}", obj.shape_id(), prop_name_si);
|
||||
let ic_pc = self.pc;
|
||||
let val = self.promote_if_needed_for_write_ptr(obj as *mut JsObject, val);
|
||||
if let Some(pos) = self.kernel_core.shape_forge().lookup_position(obj.shape_id(), prop_name_si) {
|
||||
if obj.has_prop_meta() {
|
||||
self.ordinary_set(obj, prop_name_si, val, receiver)?;
|
||||
return Ok(());
|
||||
}
|
||||
obj.set_prop_shape(pos, val);
|
||||
crate::ic_helper::write_ic_back(self.bytecode_mut(), ic_pc, obj.shape_id(), pos, 0);
|
||||
} else {
|
||||
if obj.has_prop_meta() {
|
||||
self.ordinary_set(obj, prop_name_si, val, receiver)?;
|
||||
return Ok(());
|
||||
}
|
||||
if crate::ic_helper::ic_set_hit_own(obj, &self.bytecode, ext_pc, val) {
|
||||
self.profiling.record_ic_hit();
|
||||
return Ok(());
|
||||
}
|
||||
self.profiling.record_ic_miss();
|
||||
if let Some(pos) = self.kernel_core.shape_forge().lookup_position(obj.shape_id(), prop_name_si) {
|
||||
obj.set_prop_shape(pos, val);
|
||||
crate::ic_helper::write_ic_back(self.bytecode_mut(), ext_pc, obj.shape_id(), pos, 0);
|
||||
} else {
|
||||
self.create_named_prop_fast(obj, prop_name_si, val, receiver, ext_pc, false)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// 写新属性的 CreateDataProperty 快路径(shape 转换,IC 命中在数学上无法覆盖):
|
||||
/// 原型链同名 accessor/只读检查后,一次 make_shape + 追加槽 + IC 写回 + 模板 upsert。
|
||||
///
|
||||
/// # 边界与前提
|
||||
/// - 调用方已确认属性不存在于 shape 链(lookup_position miss)。
|
||||
/// - 数组 length / 数组与 TA 整数索引键不进入本路径(调用方先分流到 ordinary_set)。
|
||||
/// - `use_frame_push` 与 ordinary_set* 入口一致(member 写 false / IC_SET 分发 true)。
|
||||
pub(crate) fn create_named_prop_fast(
|
||||
&mut self, obj: &mut JsObject, prop_name_si: u32, val: JsValue, receiver: JsValue, ext_pc: usize,
|
||||
use_frame_push: bool,
|
||||
) -> Result<(), String> {
|
||||
// 原型链同名 accessor 须触发 setter、只读 data 须报错(与 ordinary_set 一致),
|
||||
// 命中时不得创建 own 属性(规范 [[Set]] shadow 语义)。
|
||||
if let Some(meta) = self.inherited_property_meta(obj, prop_name_si) {
|
||||
if meta.is_accessor {
|
||||
if meta.set.is_undefined() {
|
||||
return self.raise_type_error("property has no setter");
|
||||
}
|
||||
return self.call_or_push_setter(meta.set, receiver, val, use_frame_push);
|
||||
}
|
||||
if !meta.attributes.writable() {
|
||||
return self.raise_type_error("cannot assign to read-only property");
|
||||
}
|
||||
}
|
||||
// 新 shape 槽位 = 追加前命名属性数(与 push_prop 的追加位置一致)。
|
||||
let slot = obj.prop_vec_len() as u32;
|
||||
let new_shape_id = self.kernel_core.shape_forge().make_shape(obj.shape_id(), prop_name_si);
|
||||
obj.set_shape_id(new_shape_id);
|
||||
obj.push_prop(val);
|
||||
obj.bump_generation();
|
||||
crate::ic_helper::write_ic_back(self.bytecode_mut(), ext_pc, new_shape_id, slot, 0);
|
||||
self.kernel_core.prop_forge().upsert(
|
||||
new_shape_id,
|
||||
PropTemplate {
|
||||
shape_id: new_shape_id,
|
||||
prop_name: prop_name_si,
|
||||
position: slot,
|
||||
generation: obj.generation(),
|
||||
},
|
||||
);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// 写新属性须分流到完整 ordinary_set 语义的场景:数组 length(ArraySetLength)与
|
||||
/// 数组/TA 整数索引键(元素区 / buffer 写)——这些键不在 shape 链上,CreateDataProperty
|
||||
/// 快路径会错误地为其建命名属性。
|
||||
pub(crate) fn named_prop_create_needs_ordinary_set(&self, obj: &JsObject, prop_name_si: u32) -> bool {
|
||||
(obj.is_array() && prop_name_si == self.length_si)
|
||||
|| (obj.is_typed_array_obj() || obj.is_array())
|
||||
&& self.array_index_from_property_key(prop_name_si).is_some()
|
||||
}
|
||||
|
||||
pub(crate) fn set_or_create_prop_value(&mut self, obj: &mut JsObject, prop_name_si: u32, val: JsValue) {
|
||||
vm_trace!("set_or_create_prop_value: shape_id={} prop_name_si={}", obj.shape_id(), prop_name_si);
|
||||
// TypedArray 整数索引写 buffer(越界忽略),不进入 shape/prop 槽。
|
||||
|
||||
Reference in New Issue
Block a user