perf: 函数调用 bytecode 改 Arc 共享(O(1) 帧切换 + IC 写时复制)

This commit is contained in:
2026-08-10 15:08:51 +08:00
parent 34fb7c8dff
commit 7efb26bc96
12 changed files with 72 additions and 50 deletions
+3 -2
View File
@@ -5,6 +5,7 @@
//! 或由其它模块克隆复制。`Display` 输出可读的反汇编文本,供调试用。 //! 或由其它模块克隆复制。`Display` 输出可读的反汇编文本,供调试用。
use std::fmt; use std::fmt;
use std::sync::Arc;
use crate::opcode::{self, OpCode}; use crate::opcode::{self, OpCode};
@@ -47,7 +48,7 @@ pub struct UpvalueCapture {
/// - `is_class_constructor` / `is_derived_constructor` / `needs_home_object` — 类相关; /// - `is_class_constructor` / `is_derived_constructor` / `needs_home_object` — 类相关;
/// - `upvalue_captures` / `cells_needed` — 闭包捕获描述。 /// - `upvalue_captures` / `cells_needed` — 闭包捕获描述。
pub struct CompiledModule { pub struct CompiledModule {
pub bytecode: Vec<opcode::Instr>, pub bytecode: Arc<[opcode::Instr]>,
pub constants: Vec<Constant>, pub constants: Vec<Constant>,
pub n_registers: u8, pub n_registers: u8,
pub n_args: u8, pub n_args: u8,
@@ -83,7 +84,7 @@ impl CompiledModule {
/// 构造空模块:空字节码、空常量池、零寄存器与全部标志默认关闭。 /// 构造空模块:空字节码、空常量池、零寄存器与全部标志默认关闭。
pub fn new() -> Self { pub fn new() -> Self {
Self { Self {
bytecode: Vec::new(), bytecode: Arc::from(Vec::new()),
constants: Vec::new(), constants: Vec::new(),
n_registers: 0, n_registers: 0,
n_args: 0, n_args: 0,
+4 -4
View File
@@ -168,12 +168,12 @@ mod tests {
fn cache_hit_debug_verifies_bytecode() { fn cache_hit_debug_verifies_bytecode() {
let forge = forge(16); let forge = forge(16);
let mut m1 = CompiledModule::new(); let mut m1 = CompiledModule::new();
m1.bytecode = vec![1, 2, 3]; m1.bytecode = Arc::from(vec![1, 2, 3]);
forge.insert(42, m1); forge.insert(42, m1);
let _ = forge let _ = forge
.get_or_insert_with(42, || { .get_or_insert_with(42, || {
let mut m = CompiledModule::new(); let mut m = CompiledModule::new();
m.bytecode = vec![4, 5, 6]; m.bytecode = Arc::from(vec![4, 5, 6]);
Ok(m) Ok(m)
}) })
.unwrap(); .unwrap();
@@ -183,12 +183,12 @@ mod tests {
fn cache_hit_returns_cached_not_recompiled() { fn cache_hit_returns_cached_not_recompiled() {
let forge = forge(16); let forge = forge(16);
let mut m = CompiledModule::new(); let mut m = CompiledModule::new();
m.bytecode = vec![1, 2, 3]; m.bytecode = Arc::from(vec![1, 2, 3]);
let cached = forge.insert(99, m); let cached = forge.insert(99, m);
let result = forge let result = forge
.get_or_insert_with(99, || { .get_or_insert_with(99, || {
let mut m = CompiledModule::new(); let mut m = CompiledModule::new();
m.bytecode = vec![1, 2, 3]; m.bytecode = Arc::from(vec![1, 2, 3]);
Ok(m) Ok(m)
}) })
.unwrap(); .unwrap();
+5 -3
View File
@@ -6,6 +6,8 @@
//! imm16 重写为对应子模块的 `flat_id`。运行时以 `flat_id` 为平表下标,闭包自足, //! imm16 重写为对应子模块的 `flat_id`。运行时以 `flat_id` 为平表下标,闭包自足,
//! 不再依赖调用上下文。 //! 不再依赖调用上下文。
use std::sync::Arc;
use oxide_bytecode::module::CompiledModule; use oxide_bytecode::module::CompiledModule;
use oxide_bytecode::opcode::{self, OpCode}; use oxide_bytecode::opcode::{self, OpCode};
@@ -20,7 +22,7 @@ fn assign_ids(module: &mut CompiledModule, next_id: &mut u32) {
sub.flat_id = *next_id; sub.flat_id = *next_id;
*next_id += 1; *next_id += 1;
} }
for instr in &mut module.bytecode { for instr in Arc::make_mut(&mut module.bytecode).iter_mut() {
if opcode::opcode(*instr) == OpCode::CREATE_CLOSURE { if opcode::opcode(*instr) == OpCode::CREATE_CLOSURE {
let rel = opcode::imm16(*instr) as usize; let rel = opcode::imm16(*instr) as usize;
// rel 应为 1-based 子模块下标;0 表示无引用(异常输入),按未解析处理。 // rel 应为 1-based 子模块下标;0 表示无引用(异常输入),按未解析处理。
@@ -51,10 +53,10 @@ mod tests {
#[test] #[test]
fn flatten_assigns_global_ids_and_rewrites_closures() { fn flatten_assigns_global_ids_and_rewrites_closures() {
let mut leaf = CompiledModule::new(); let mut leaf = CompiledModule::new();
leaf.bytecode = vec![opcode::encode(OpCode::CREATE_CLOSURE, 1, 1, 0)]; leaf.bytecode = Arc::from(vec![opcode::encode(OpCode::CREATE_CLOSURE, 1, 1, 0)]);
let mut mid = CompiledModule::new(); let mut mid = CompiledModule::new();
mid.sub_modules = vec![leaf]; mid.sub_modules = vec![leaf];
mid.bytecode = vec![opcode::encode(OpCode::CREATE_CLOSURE, 2, 1, 0)]; mid.bytecode = Arc::from(vec![opcode::encode(OpCode::CREATE_CLOSURE, 2, 1, 0)]);
let mut top = module_with_closure(vec![mid]); let mut top = module_with_closure(vec![mid]);
flatten_submodules(&mut top); flatten_submodules(&mut top);
+3 -1
View File
@@ -4,6 +4,8 @@
//! label 用前缀和:`label_pos` 记 Inst 下标,平铺时维护 inst→instr 映射, //! label 用前缀和:`label_pos` 记 Inst 下标,平铺时维护 inst→instr 映射,
//! 跳转 offset 以 Instr(含 ext 字)为单位。 //! 跳转 offset 以 Instr(含 ext 字)为单位。
use std::sync::Arc;
use oxide_bytecode::module::CompiledModule; use oxide_bytecode::module::CompiledModule;
use oxide_bytecode::opcode::{self, OpCode}; use oxide_bytecode::opcode::{self, OpCode};
@@ -75,7 +77,7 @@ pub fn lower(f: &IRFunction) -> Result<CompiledModule, String> {
} }
Ok(CompiledModule { Ok(CompiledModule {
bytecode: instrs, bytecode: Arc::from(instrs),
constants: f.constants.clone(), constants: f.constants.clone(),
n_registers: f.n_registers as u8, n_registers: f.n_registers as u8,
n_args: f.param_layout.count as u8, n_args: f.param_layout.count as u8,
+1 -1
View File
@@ -482,7 +482,7 @@ impl Vm {
return Err(self.error_message_text("RangeError", "Maximum call stack size exceeded")); return Err(self.error_message_text("RangeError", "Maximum call stack size exceeded"));
} }
let sub_bytecode = self.sub_modules[sub_idx].bytecode.clone(); let sub_bytecode = Arc::clone(&self.sub_modules[sub_idx].bytecode);
let sub_n_args = self.sub_modules[sub_idx].n_args as usize; let sub_n_args = self.sub_modules[sub_idx].n_args as usize;
let sub_n_registers = self.sub_modules[sub_idx].n_registers; let sub_n_registers = self.sub_modules[sub_idx].n_registers;
let sub_param_base = self.sub_modules[sub_idx].param_base as usize; let sub_param_base = self.sub_modules[sub_idx].param_base as usize;
+7 -5
View File
@@ -305,6 +305,7 @@ impl Vm {
let obj = unsafe { &*obj_ptr }; let obj = unsafe { &*obj_ptr };
let prop_name_si = self.property_key_si(self.regs[b]); let prop_name_si = self.property_key_si(self.regs[b]);
let (cached_shape_id, cached_slot, cached_depth) = ic_helper::read_ic_entry(&self.bytecode, &mut self.pc); let (cached_shape_id, cached_slot, cached_depth) = ic_helper::read_ic_entry(&self.bytecode, &mut self.pc);
let ic_pc = self.pc;
if obj.has_prop_meta() { if obj.has_prop_meta() {
let val = self.ordinary_get_with_target(obj, prop_name_si, val, a as u8)?; let val = self.ordinary_get_with_target(obj, prop_name_si, val, a as u8)?;
if self.accessor_frame_target_reg.take().is_none() { if self.accessor_frame_target_reg.take().is_none() {
@@ -322,7 +323,7 @@ impl Vm {
prop_cache_miss(); prop_cache_miss();
if template.prop_name == prop_name_si { if template.prop_name == prop_name_si {
if template.position < obj.prop_vec_len() as u32 { if template.position < obj.prop_vec_len() as u32 {
ic_helper::write_ic_back(&mut self.bytecode, self.pc, obj.shape_id(), template.position, 0); ic_helper::write_ic_back(self.bytecode_mut(), ic_pc, obj.shape_id(), template.position, 0);
ic_debug!( ic_debug!(
"IC_GET propforge hit shape={} prop={} slot={}", "IC_GET propforge hit shape={} prop={} slot={}",
obj.shape_id(), obj.shape_id(),
@@ -345,7 +346,7 @@ impl Vm {
// 先试自身属性(快路径,depth=0)。 // 先试自身属性(快路径,depth=0)。
if let Some(pos) = self.kernel_core.shape_forge().lookup_position(obj.shape_id(), prop_name_si) { if let Some(pos) = self.kernel_core.shape_forge().lookup_position(obj.shape_id(), prop_name_si) {
if !obj.is_accessor_meta(pos) { if !obj.is_accessor_meta(pos) {
ic_helper::write_ic_back(&mut self.bytecode, self.pc, obj.shape_id(), pos, 0); ic_helper::write_ic_back(self.bytecode_mut(), ic_pc, obj.shape_id(), pos, 0);
ic_debug!("IC_GET write-back own shape={} slot={}", obj.shape_id(), pos); ic_debug!("IC_GET write-back own shape={} slot={}", obj.shape_id(), pos);
} }
} else { } else {
@@ -356,7 +357,7 @@ impl Vm {
let co_ref = unsafe { &*cursor }; let co_ref = unsafe { &*cursor };
if let Some(pos) = self.kernel_core.shape_forge().lookup_position(co_ref.shape_id(), prop_name_si) { if let Some(pos) = self.kernel_core.shape_forge().lookup_position(co_ref.shape_id(), prop_name_si) {
if !co_ref.is_accessor_meta(pos) { if !co_ref.is_accessor_meta(pos) {
ic_helper::write_ic_back(&mut self.bytecode, self.pc, co_ref.shape_id(), pos, depth); ic_helper::write_ic_back(self.bytecode_mut(), ic_pc, co_ref.shape_id(), pos, depth);
ic_debug!( ic_debug!(
"IC_GET write-back proto shape={} slot={} depth={}", "IC_GET write-back proto shape={} slot={} depth={}",
co_ref.shape_id(), co_ref.shape_id(),
@@ -399,6 +400,7 @@ impl Vm {
} }
let (cached_shape_id, cached_slot, cached_depth) = ic_helper::read_ic_entry(&self.bytecode, &mut self.pc); let (cached_shape_id, cached_slot, cached_depth) = ic_helper::read_ic_entry(&self.bytecode, &mut self.pc);
let ic_pc = self.pc;
let value = self.promote_if_needed_for_write_ptr(obj_ptr, self.regs[a]); let value = self.promote_if_needed_for_write_ptr(obj_ptr, self.regs[a]);
let receiver = self.regs[rd]; let receiver = self.regs[rd];
let obj = unsafe { &mut *obj_ptr }; let obj = unsafe { &mut *obj_ptr };
@@ -414,7 +416,7 @@ impl Vm {
self.profiling.record_ic_miss(); self.profiling.record_ic_miss();
prop_cache_miss(); prop_cache_miss();
obj.set_prop_shape(pos, value); obj.set_prop_shape(pos, value);
ic_helper::write_ic_back(&mut self.bytecode, self.pc, obj.shape_id(), pos, 0); 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); ic_debug!("IC_SET write-back shape={} slot={}", obj.shape_id(), pos);
} else { } else {
self.profiling.record_ic_miss(); self.profiling.record_ic_miss();
@@ -423,7 +425,7 @@ impl Vm {
self.ordinary_set_dispatch(obj, prop_name_si, value, receiver)?; self.ordinary_set_dispatch(obj, prop_name_si, value, receiver)?;
if old_shape != obj.shape_id() { if old_shape != obj.shape_id() {
if let Some(pos) = self.kernel_core.shape_forge().lookup_position(obj.shape_id(), prop_name_si) { if let Some(pos) = self.kernel_core.shape_forge().lookup_position(obj.shape_id(), prop_name_si) {
ic_helper::write_ic_back(&mut self.bytecode, self.pc, obj.shape_id(), pos, 0); 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); ic_debug!("IC_SET write-back shape={} slot={}", obj.shape_id(), pos);
self.kernel_core.prop_forge().upsert( self.kernel_core.prop_forge().upsert(
obj.shape_id(), obj.shape_id(),
+5 -5
View File
@@ -18,7 +18,7 @@ use crate::vm::{CallFrame, Completion, ForInIter, TryHandler, Vm};
pub(crate) struct SuspendedFrame { pub(crate) struct SuspendedFrame {
pub regs: Box<[JsValue; 256]>, pub regs: Box<[JsValue; 256]>,
pub pc: usize, pub pc: usize,
pub bytecode: Vec<opcode::Instr>, pub bytecode: Arc<[opcode::Instr]>,
/// 模块 flat_id(恢复时经 sub_modules 重激活 immutables;跨 run 失效据此报错)。 /// 模块 flat_id(恢复时经 sub_modules 重激活 immutables;跨 run 失效据此报错)。
pub sub_idx: u32, pub sub_idx: u32,
pub active_reg_limit: u8, pub active_reg_limit: u8,
@@ -34,7 +34,7 @@ pub(crate) struct SuspendedFrame {
pub last_for_of_result: JsValue, pub last_for_of_result: JsValue,
/// `yield*` 委托中的内层迭代器;异步函数恒为 None。 /// `yield*` 委托中的内层迭代器;异步函数恒为 None。
pub delegated_iterator: Option<JsValue>, pub delegated_iterator: Option<JsValue>,
pub saved_bytecode_stack: Vec<Vec<opcode::Instr>>, pub saved_bytecode_stack: Vec<Arc<[opcode::Instr]>>,
pub saved_immutables_stack: Vec<*const [JsValue]>, pub saved_immutables_stack: Vec<*const [JsValue]>,
pub exception_value: Option<JsValue>, pub exception_value: Option<JsValue>,
pub pending_exception: Option<JsValue>, pub pending_exception: Option<JsValue>,
@@ -47,7 +47,7 @@ impl SuspendedFrame {
pub fn new_empty() -> Self { SuspendedFrame { pub fn new_empty() -> Self { SuspendedFrame {
regs: Box::new([JsValue::undefined(); 256]), regs: Box::new([JsValue::undefined(); 256]),
pc: 0, pc: 0,
bytecode: Vec::new(), bytecode: Arc::default(),
sub_idx: 0, sub_idx: 0,
active_reg_limit: 0, active_reg_limit: 0,
root_reg_limit: 0, root_reg_limit: 0,
@@ -349,7 +349,7 @@ mod tests {
frame.regs[0] = JsValue::float(1.0); frame.regs[0] = JsValue::float(1.0);
frame.regs[7] = JsValue::float(2.0); frame.regs[7] = JsValue::float(2.0);
frame.pc = 11; frame.pc = 11;
frame.bytecode = vec![0u32]; frame.bytecode = Arc::from(vec![0u32]);
frame.sub_idx = 3; frame.sub_idx = 3;
frame.active_reg_limit = 4; frame.active_reg_limit = 4;
frame.root_reg_limit = 5; frame.root_reg_limit = 5;
@@ -367,7 +367,7 @@ mod tests {
frame.for_of_iters.push(JsValue::float(6.0)); frame.for_of_iters.push(JsValue::float(6.0));
frame.last_for_of_result = JsValue::float(7.0); frame.last_for_of_result = JsValue::float(7.0);
frame.delegated_iterator = Some(JsValue::float(8.0)); frame.delegated_iterator = Some(JsValue::float(8.0));
frame.saved_bytecode_stack.push(vec![0u32]); frame.saved_bytecode_stack.push(Arc::from(vec![0u32]));
frame.saved_immutables_stack.push(std::ptr::slice_from_raw_parts(std::ptr::null(), 0)); frame.saved_immutables_stack.push(std::ptr::slice_from_raw_parts(std::ptr::null(), 0));
frame.exception_value = Some(JsValue::float(9.0)); frame.exception_value = Some(JsValue::float(9.0));
frame.pending_exception = Some(JsValue::float(10.0)); frame.pending_exception = Some(JsValue::float(10.0));
+28 -16
View File
@@ -197,7 +197,7 @@ impl Completion {
pub(crate) struct InlineSyncState { pub(crate) struct InlineSyncState {
pub(crate) regs: Box<[JsValue; 256]>, pub(crate) regs: Box<[JsValue; 256]>,
pub(crate) pc: usize, pub(crate) pc: usize,
pub(crate) bytecode: Vec<opcode::Instr>, pub(crate) bytecode: Arc<[opcode::Instr]>,
pub(crate) active_immutables: *const [JsValue], pub(crate) active_immutables: *const [JsValue],
pub(crate) active_reg_limit: u8, pub(crate) active_reg_limit: u8,
pub(crate) root_reg_limit: u8, pub(crate) root_reg_limit: u8,
@@ -210,7 +210,7 @@ pub(crate) struct InlineSyncState {
pub(crate) for_in_iters: Vec<*mut ForInIter<'static>>, pub(crate) for_in_iters: Vec<*mut ForInIter<'static>>,
pub(crate) for_of_iters: Vec<JsValue>, pub(crate) for_of_iters: Vec<JsValue>,
pub(crate) last_for_of_result: JsValue, pub(crate) last_for_of_result: JsValue,
pub(crate) saved_bytecode_stack: Vec<Vec<opcode::Instr>>, pub(crate) saved_bytecode_stack: Vec<Arc<[opcode::Instr]>>,
pub(crate) saved_immutables_stack: Vec<*const [JsValue]>, pub(crate) saved_immutables_stack: Vec<*const [JsValue]>,
pub(crate) save_stack: Vec<JsValue>, pub(crate) save_stack: Vec<JsValue>,
pub(crate) spill_stack: Vec<JsValue>, pub(crate) spill_stack: Vec<JsValue>,
@@ -229,7 +229,10 @@ pub(crate) struct InlineSyncState {
pub struct Vm { pub struct Vm {
pub(crate) regs: [JsValue; 256], pub(crate) regs: [JsValue; 256],
pub(crate) pc: usize, pub(crate) pc: usize,
pub(crate) bytecode: Vec<opcode::Instr>, /// 当前活动字节码。以 `Arc<[Instr]>` 共享:函数调用经 `Arc::clone` 换帧(O(1)),
/// 不再逐帧深拷贝。与 sub_modules 源共享同一缓冲,IC 写回经 `bytecode_mut` 的
/// `Arc::make_mut` 写时复制,保证独占后才改写(miss 时才深拷贝,频率低)。
pub(crate) bytecode: Arc<[opcode::Instr]>,
/// 每次 run 转换一次的不可变常量缓存。下标 0 = 顶层模块,sub_idx+1 = sub_modules[sub_idx]。 /// 每次 run 转换一次的不可变常量缓存。下标 0 = 顶层模块,sub_idx+1 = sub_modules[sub_idx]。
/// 每个 `OnceLock` 保存该模块常量本次运行中只转换一次的 `JsValue` 结果,每次 `run()` 重建。 /// 每个 `OnceLock` 保存该模块常量本次运行中只转换一次的 `JsValue` 结果,每次 `run()` 重建。
/// 不可变常量是标量 + perm 字符串,只读,不作为 GC 根。 /// 不可变常量是标量 + perm 字符串,只读,不作为 GC 根。
@@ -267,7 +270,8 @@ pub struct Vm {
/// 全局扁平模块表:下标 = 模块 `flat_id`(顶层 0,子模块 flatten 后全局唯一)。 /// 全局扁平模块表:下标 = 模块 `flat_id`(顶层 0,子模块 flatten 后全局唯一)。
/// 闭包 `sub_module_index` 即 flat_id,逃逸闭包也能自足解析。 /// 闭包 `sub_module_index` 即 flat_id,逃逸闭包也能自足解析。
pub(crate) sub_modules: Arc<Vec<CompiledModule>>, pub(crate) sub_modules: Arc<Vec<CompiledModule>>,
pub(crate) saved_bytecode_stack: Vec<Vec<opcode::Instr>>, /// 帧切换时暂存调用方字节码的 Arc 栈(与 `bytecode` 同共享语义)。
pub(crate) saved_bytecode_stack: Vec<Arc<[opcode::Instr]>>,
pub(crate) saved_immutables_stack: Vec<*const [JsValue]>, pub(crate) saved_immutables_stack: Vec<*const [JsValue]>,
/// 共享寄存器保存栈。每个活动 `CallFrame` 在 push 时把调用方活跃寄存器 /// 共享寄存器保存栈。每个活动 `CallFrame` 在 push 时把调用方活跃寄存器
/// (`regs[..caller_reg_limit]`)按 `saved_reg_offset` 存到这里;恢复时复制回 /// (`regs[..caller_reg_limit]`)按 `saved_reg_offset` 存到这里;恢复时复制回
@@ -521,6 +525,13 @@ impl Vm {
self.active_immutables = vec.as_slice() as *const [JsValue]; self.active_immutables = vec.as_slice() as *const [JsValue];
} }
/// 当前活动字节码的可变访问入口。bytecode 以 `Arc<[Instr]>` 与 sub_modules 源共享,
/// IC 写回经 `Arc::make_mut` 保证独占:独占时零拷贝原地写,共享时先深拷贝再写
/// (IC miss 才触发,频率低)。所有写操作必须经此方法,防止共享缓冲被多实例污染。
pub(crate) fn bytecode_mut(&mut self) -> &mut [opcode::Instr] {
Arc::make_mut(&mut self.bytecode)
}
/// GC 根收集的统一遍历(对象与字符串都产出)。与 `rewrite_values` 字段一一对应。 /// GC 根收集的统一遍历(对象与字符串都产出)。与 `rewrite_values` 字段一一对应。
/// 覆盖执行核心的全部 JsValue 持有点:regs/帧/各栈段/cell/在途异常与完成/ /// 覆盖执行核心的全部 JsValue 持有点:regs/帧/各栈段/cell/在途异常与完成/
/// 挂起信号/迭代器/微任务/global。新增执行字段必须同时登记在此与 /// 挂起信号/迭代器/微任务/global。新增执行字段必须同时登记在此与
@@ -1036,7 +1047,7 @@ impl Vm {
return self.raise_error_kind("RangeError", "Maximum call stack size exceeded"); return self.raise_error_kind("RangeError", "Maximum call stack size exceeded");
} }
let sub_bytecode = self.sub_modules[sub_idx].bytecode.clone(); let sub_bytecode = Arc::clone(&self.sub_modules[sub_idx].bytecode);
let sub_n_args = self.sub_modules[sub_idx].n_args as usize; let sub_n_args = self.sub_modules[sub_idx].n_args as usize;
let sub_n_registers = self.sub_modules[sub_idx].n_registers; let sub_n_registers = self.sub_modules[sub_idx].n_registers;
let sub_param_base = self.sub_modules[sub_idx].param_base as usize; let sub_param_base = self.sub_modules[sub_idx].param_base as usize;
@@ -1902,7 +1913,7 @@ impl Vm {
/// 原子树 flat_id 自 1 连续,因此拷贝顺序即新 id 顺序,`out` 下标对齐平表槽位。 /// 原子树 flat_id 自 1 连续,因此拷贝顺序即新 id 顺序,`out` 下标对齐平表槽位。
fn rehome_subtree(module: &CompiledModule, base: u32, out: &mut Vec<CompiledModule>) { fn rehome_subtree(module: &CompiledModule, base: u32, out: &mut Vec<CompiledModule>) {
let new_id = base + module.flat_id - 1; let new_id = base + module.flat_id - 1;
let mut bytecode = module.bytecode.clone(); let mut bytecode = module.bytecode.to_vec();
for instr in &mut bytecode { for instr in &mut bytecode {
if opcode::opcode(*instr) == OpCode::CREATE_CLOSURE { if opcode::opcode(*instr) == OpCode::CREATE_CLOSURE {
let old = opcode::imm16(*instr) as u32; let old = opcode::imm16(*instr) as u32;
@@ -1916,7 +1927,7 @@ fn rehome_subtree(module: &CompiledModule, base: u32, out: &mut Vec<CompiledModu
} }
} }
let mut rehomed = module.clone(); let mut rehomed = module.clone();
rehomed.bytecode = bytecode; rehomed.bytecode = Arc::from(bytecode);
rehomed.flat_id = new_id; rehomed.flat_id = new_id;
out.push(rehomed); out.push(rehomed);
for sub in &module.sub_modules { for sub in &module.sub_modules {
@@ -1927,6 +1938,7 @@ fn rehome_subtree(module: &CompiledModule, base: u32, out: &mut Vec<CompiledModu
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::{opcode, JsValue, TryHandler, Vm}; use super::{opcode, JsValue, TryHandler, Vm};
use std::sync::Arc;
use oxide_bytecode::module::CompiledModule; use oxide_bytecode::module::CompiledModule;
use oxide_runtime_api::NativeResult; use oxide_runtime_api::NativeResult;
use oxide_types::object::NativeFnPtr; use oxide_types::object::NativeFnPtr;
@@ -2025,7 +2037,7 @@ mod tests {
.push(std::ptr::dangling_mut::<super::ForInIter<'static>>()); .push(std::ptr::dangling_mut::<super::ForInIter<'static>>());
vm.iters.for_of_iters.push(JsValue::undefined()); vm.iters.for_of_iters.push(JsValue::undefined());
vm.saved_bytecode_stack vm.saved_bytecode_stack
.push(vec![opcode::encode(opcode::OpCode::HALT, 0, 0, 0)]); .push(Arc::from(vec![opcode::encode(opcode::OpCode::HALT, 0, 0, 0)]));
vm.saved_immutables_stack vm.saved_immutables_stack
.push(std::ptr::slice_from_raw_parts(std::ptr::null(), 0)); .push(std::ptr::slice_from_raw_parts(std::ptr::null(), 0));
vm.try_stack.push(TryHandler { vm.try_stack.push(TryHandler {
@@ -2071,10 +2083,10 @@ mod tests {
#[test] #[test]
fn for_of_close_pops_iterator_stack() { fn for_of_close_pops_iterator_stack() {
let module = CompiledModule { let module = CompiledModule {
bytecode: vec![ bytecode: Arc::from(vec![
opcode::encode(opcode::OpCode::FOR_OF_CLOSE, 0, 0, 0), opcode::encode(opcode::OpCode::FOR_OF_CLOSE, 0, 0, 0),
opcode::encode(opcode::OpCode::HALT, 0, 0, 0), opcode::encode(opcode::OpCode::HALT, 0, 0, 0),
], ]),
n_registers: 1, n_registers: 1,
..CompiledModule::new() ..CompiledModule::new()
}; };
@@ -2089,9 +2101,9 @@ mod tests {
#[test] #[test]
fn write_ic_back_updates_three_extension_words() { fn write_ic_back_updates_three_extension_words() {
let mut vm = Vm::new(); let mut vm = Vm::new();
vm.bytecode = vec![0, 0, 0]; vm.bytecode = Arc::from(vec![0, 0, 0]);
vm.pc = 3; vm.pc = 3;
crate::ic_helper::write_ic_back(&mut vm.bytecode, vm.pc, 0x1234_5678, 7, 0); crate::ic_helper::write_ic_back(Arc::make_mut(&mut vm.bytecode), vm.pc, 0x1234_5678, 7, 0);
assert_eq!(vm.bytecode[0], 0x0034_5678); assert_eq!(vm.bytecode[0], 0x0034_5678);
assert_eq!(vm.bytecode[1], 7); assert_eq!(vm.bytecode[1], 7);
assert_eq!(vm.bytecode[2], 0); assert_eq!(vm.bytecode[2], 0);
@@ -2100,9 +2112,9 @@ mod tests {
#[test] #[test]
fn write_ic_back_stores_proto_depth() { fn write_ic_back_stores_proto_depth() {
let mut vm = Vm::new(); let mut vm = Vm::new();
vm.bytecode = vec![0, 0, 0]; vm.bytecode = Arc::from(vec![0, 0, 0]);
vm.pc = 3; vm.pc = 3;
crate::ic_helper::write_ic_back(&mut vm.bytecode, vm.pc, 0xAAAA_BBBB, 42, 2); crate::ic_helper::write_ic_back(Arc::make_mut(&mut vm.bytecode), vm.pc, 0xAAAA_BBBB, 42, 2);
assert_eq!(vm.bytecode[0], 0x00AA_BBBB); assert_eq!(vm.bytecode[0], 0x00AA_BBBB);
assert_eq!(vm.bytecode[1], 42); assert_eq!(vm.bytecode[1], 42);
assert_eq!(vm.bytecode[2], 2); assert_eq!(vm.bytecode[2], 2);
@@ -2111,10 +2123,10 @@ mod tests {
#[test] #[test]
fn unimplemented_profile_opcode_fails_explicitly() { fn unimplemented_profile_opcode_fails_explicitly() {
let module = CompiledModule { let module = CompiledModule {
bytecode: vec![ bytecode: Arc::from(vec![
opcode::encode(opcode::OpCode::PROFILE_SHAPE, 0, 0, 0), opcode::encode(opcode::OpCode::PROFILE_SHAPE, 0, 0, 0),
opcode::encode(opcode::OpCode::HALT, 0, 0, 0), opcode::encode(opcode::OpCode::HALT, 0, 0, 0),
], ]),
n_registers: 1, n_registers: 1,
..CompiledModule::new() ..CompiledModule::new()
}; };
+1 -1
View File
@@ -109,7 +109,7 @@ impl Vm {
} }
let new_obj_val = JsValue::object(new_obj as *mut u8); let new_obj_val = JsValue::object(new_obj as *mut u8);
let sub_bytecode = self.sub_modules[sub_idx].bytecode.clone(); let sub_bytecode = Arc::clone(&self.sub_modules[sub_idx].bytecode);
let sub_n_args = self.sub_modules[sub_idx].n_args as usize; let sub_n_args = self.sub_modules[sub_idx].n_args as usize;
let sub_n_registers = self.sub_modules[sub_idx].n_registers; let sub_n_registers = self.sub_modules[sub_idx].n_registers;
let sub_param_base = self.sub_modules[sub_idx].param_base as usize; let sub_param_base = self.sub_modules[sub_idx].param_base as usize;
+7 -4
View File
@@ -278,6 +278,7 @@ impl Vm {
let ext1 = self.bytecode[self.pc + 1]; let ext1 = self.bytecode[self.pc + 1];
let ext2 = self.bytecode[self.pc + 2]; let ext2 = self.bytecode[self.pc + 2];
self.pc += 3; self.pc += 3;
let ic_pc = self.pc;
if obj.has_prop_meta() { if obj.has_prop_meta() {
return self.ordinary_get(obj, prop_name_si, receiver); return self.ordinary_get(obj, prop_name_si, receiver);
} }
@@ -291,7 +292,7 @@ impl Vm {
if template.prop_name != prop_name_si { if template.prop_name != prop_name_si {
self.proto_chain_ic_get(obj, prop_name_si, receiver)? self.proto_chain_ic_get(obj, prop_name_si, receiver)?
} else if template.position < obj.prop_vec_len() as u32 { } else if template.position < obj.prop_vec_len() as u32 {
crate::ic_helper::write_ic_back(&mut self.bytecode, self.pc, obj.shape_id(), template.position, 0); crate::ic_helper::write_ic_back(self.bytecode_mut(), ic_pc, obj.shape_id(), template.position, 0);
obj.get_prop_shape(template.position) obj.get_prop_shape(template.position)
} else { } else {
self.proto_chain_ic_get(obj, prop_name_si, receiver)? self.proto_chain_ic_get(obj, prop_name_si, receiver)?
@@ -304,11 +305,12 @@ impl Vm {
/// 执行 ordinary_get 并把 IC 按原型链深度写回。 /// 执行 ordinary_get 并把 IC 按原型链深度写回。
fn proto_chain_ic_get(&mut self, obj: &JsObject, prop_name_si: u32, receiver: JsValue) -> Result<JsValue, String> { fn proto_chain_ic_get(&mut self, obj: &JsObject, prop_name_si: u32, receiver: JsValue) -> Result<JsValue, String> {
let ic_pc = self.pc;
let resolved = self.ordinary_get(obj, prop_name_si, receiver)?; let resolved = self.ordinary_get(obj, prop_name_si, receiver)?;
// 快路径:自身属性(depth=0)。 // 快路径:自身属性(depth=0)。
if let Some(pos) = self.kernel_core.shape_forge().lookup_position(obj.shape_id(), prop_name_si) { if let Some(pos) = self.kernel_core.shape_forge().lookup_position(obj.shape_id(), prop_name_si) {
if !obj.is_accessor_meta(pos) { if !obj.is_accessor_meta(pos) {
crate::ic_helper::write_ic_back(&mut self.bytecode, self.pc, obj.shape_id(), pos, 0); crate::ic_helper::write_ic_back(self.bytecode_mut(), ic_pc, obj.shape_id(), pos, 0);
} }
return Ok(resolved); return Ok(resolved);
} }
@@ -319,7 +321,7 @@ impl Vm {
let co = unsafe { &*cursor }; let co = unsafe { &*cursor };
if let Some(pos) = self.kernel_core.shape_forge().lookup_position(co.shape_id(), prop_name_si) { if let Some(pos) = self.kernel_core.shape_forge().lookup_position(co.shape_id(), prop_name_si) {
if !co.is_accessor_meta(pos) { if !co.is_accessor_meta(pos) {
crate::ic_helper::write_ic_back(&mut self.bytecode, self.pc, co.shape_id(), pos, depth); crate::ic_helper::write_ic_back(self.bytecode_mut(), ic_pc, co.shape_id(), pos, depth);
} }
break; break;
} }
@@ -336,6 +338,7 @@ impl Vm {
&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,
) -> Result<(), String> { ) -> Result<(), String> {
ic_trace!("set_member_prop: shape_id={} prop_name_si={}", obj.shape_id(), prop_name_si); 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); 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 let Some(pos) = self.kernel_core.shape_forge().lookup_position(obj.shape_id(), prop_name_si) {
if obj.has_prop_meta() { if obj.has_prop_meta() {
@@ -343,7 +346,7 @@ impl Vm {
return Ok(()); return Ok(());
} }
obj.set_prop_shape(pos, val); obj.set_prop_shape(pos, val);
crate::ic_helper::write_ic_back(&mut self.bytecode, self.pc, obj.shape_id(), pos, 0); crate::ic_helper::write_ic_back(self.bytecode_mut(), ic_pc, obj.shape_id(), pos, 0);
} else { } else {
self.ordinary_set(obj, prop_name_si, val, receiver)?; self.ordinary_set(obj, prop_name_si, val, receiver)?;
} }
+4 -4
View File
@@ -156,7 +156,7 @@ impl Vm {
) -> Result<(), String> { ) -> Result<(), String> {
self.regs = [JsValue::undefined(); 256]; self.regs = [JsValue::undefined(); 256];
self.pc = 0; self.pc = 0;
self.bytecode = Vec::new(); self.bytecode = Arc::default();
self.active_reg_limit = 1; self.active_reg_limit = 1;
self.root_reg_limit = 1; self.root_reg_limit = 1;
self.try_stack.clear(); self.try_stack.clear();
@@ -222,7 +222,7 @@ impl Vm {
self.regs = [JsValue::undefined(); 256]; self.regs = [JsValue::undefined(); 256];
self.pc = 0; self.pc = 0;
self.bytecode = sub.bytecode.clone(); self.bytecode = Arc::clone(&sub.bytecode);
self.activate_immutables(sub_idx, &sub.constants); self.activate_immutables(sub_idx, &sub.constants);
self.active_reg_limit = sub.n_registers.max(1); self.active_reg_limit = sub.n_registers.max(1);
self.root_reg_limit = self.active_reg_limit; self.root_reg_limit = self.active_reg_limit;
@@ -300,7 +300,7 @@ impl Vm {
vm_info!("rerun: clearing IC caches"); vm_info!("rerun: clearing IC caches");
self.clear_execution_state(); self.clear_execution_state();
self.active_reg_limit = self.root_reg_limit; self.active_reg_limit = self.root_reg_limit;
crate::ic_helper::clear_ic_caches(&mut self.bytecode); crate::ic_helper::clear_ic_caches(self.bytecode_mut());
self.dispatch() self.dispatch()
} }
@@ -315,7 +315,7 @@ impl Vm {
self.cell_stack.push(Vec::new()); self.cell_stack.push(Vec::new());
self.sub_modules = Arc::new(collect_flat_modules(module)); self.sub_modules = Arc::new(collect_flat_modules(module));
self.immutables_cache = (0..self.sub_modules.len()).map(|_| OnceLock::new()).collect(); self.immutables_cache = (0..self.sub_modules.len()).map(|_| OnceLock::new()).collect();
self.bytecode = module.bytecode.clone(); self.bytecode = Arc::clone(&module.bytecode);
self.activate_immutables(0, &module.constants); self.activate_immutables(0, &module.constants);
self.root_reg_limit = module.n_registers.max(1); self.root_reg_limit = module.n_registers.max(1);
self.active_reg_limit = self.root_reg_limit; self.active_reg_limit = self.root_reg_limit;
+4 -4
View File
@@ -25,7 +25,7 @@ impl Vm {
let mut vm = Self { let mut vm = Self {
regs: [JsValue::undefined(); 256], regs: [JsValue::undefined(); 256],
pc: 0, pc: 0,
bytecode: Vec::new(), bytecode: Arc::default(),
immutables_cache: Vec::new(), immutables_cache: Vec::new(),
active_immutables: std::ptr::slice_from_raw_parts(std::ptr::null(), 0), active_immutables: std::ptr::slice_from_raw_parts(std::ptr::null(), 0),
frames: smallvec::SmallVec::new(), frames: smallvec::SmallVec::new(),
@@ -118,7 +118,7 @@ impl Vm {
let mut vm = Self { let mut vm = Self {
regs: [JsValue::undefined(); 256], regs: [JsValue::undefined(); 256],
pc: 0, pc: 0,
bytecode: Vec::new(), bytecode: Arc::default(),
immutables_cache: Vec::new(), immutables_cache: Vec::new(),
active_immutables: std::ptr::slice_from_raw_parts(std::ptr::null(), 0), active_immutables: std::ptr::slice_from_raw_parts(std::ptr::null(), 0),
frames: smallvec::SmallVec::new(), frames: smallvec::SmallVec::new(),
@@ -266,7 +266,7 @@ impl Vm {
fn clear_full_reset_state(&mut self) { fn clear_full_reset_state(&mut self) {
self.clear_execution_state(); self.clear_execution_state();
self.bytecode.clear(); self.bytecode = Arc::default();
self.immutables_cache.clear(); self.immutables_cache.clear();
self.active_immutables = std::ptr::slice_from_raw_parts(std::ptr::null(), 0); self.active_immutables = std::ptr::slice_from_raw_parts(std::ptr::null(), 0);
self.free_epoch_object_heap_data(); self.free_epoch_object_heap_data();
@@ -334,7 +334,7 @@ impl Vm {
pub fn reset(&mut self) { pub fn reset(&mut self) {
self.clear_execution_state(); self.clear_execution_state();
self.maybe_collect_session_gc(); self.maybe_collect_session_gc();
self.bytecode.clear(); self.bytecode = Arc::default();
self.immutables_cache.clear(); self.immutables_cache.clear();
self.active_immutables = std::ptr::slice_from_raw_parts(std::ptr::null(), 0); self.active_immutables = std::ptr::slice_from_raw_parts(std::ptr::null(), 0);
self.free_epoch_object_heap_data(); self.free_epoch_object_heap_data();