mirror of
https://github.com/pjh456/OxideJS.git
synced 2026-10-23 18:21:13 +08:00
perf: run 装载期常量转换与 si 侧表改 Vm 模块级缓存
This commit is contained in:
Generated
+21
-21
@@ -1160,7 +1160,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "oxide_builtins"
|
||||
version = "0.17.6"
|
||||
version = "0.17.7"
|
||||
dependencies = [
|
||||
"chrono",
|
||||
"fast-float",
|
||||
@@ -1185,7 +1185,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "oxide_bytecode"
|
||||
version = "0.17.6"
|
||||
version = "0.17.7"
|
||||
dependencies = [
|
||||
"num-bigint",
|
||||
"oxide_log",
|
||||
@@ -1193,7 +1193,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "oxide_cfg"
|
||||
version = "0.17.6"
|
||||
version = "0.17.7"
|
||||
dependencies = [
|
||||
"oxide_bytecode",
|
||||
"oxide_emit",
|
||||
@@ -1204,7 +1204,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "oxide_cli"
|
||||
version = "0.17.6"
|
||||
version = "0.17.7"
|
||||
dependencies = [
|
||||
"ansi_term",
|
||||
"bumpalo",
|
||||
@@ -1228,7 +1228,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "oxide_code_cache"
|
||||
version = "0.17.6"
|
||||
version = "0.17.7"
|
||||
dependencies = [
|
||||
"lru",
|
||||
"oxide_bytecode",
|
||||
@@ -1237,7 +1237,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "oxide_compiler"
|
||||
version = "0.17.6"
|
||||
version = "0.17.7"
|
||||
dependencies = [
|
||||
"oxide_bytecode",
|
||||
"oxide_cfg",
|
||||
@@ -1258,7 +1258,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "oxide_dce"
|
||||
version = "0.17.6"
|
||||
version = "0.17.7"
|
||||
dependencies = [
|
||||
"oxide_bytecode",
|
||||
"oxide_cfg",
|
||||
@@ -1272,7 +1272,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "oxide_emit"
|
||||
version = "0.17.6"
|
||||
version = "0.17.7"
|
||||
dependencies = [
|
||||
"num-bigint",
|
||||
"oxc_allocator",
|
||||
@@ -1288,7 +1288,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "oxide_engine"
|
||||
version = "0.17.6"
|
||||
version = "0.17.7"
|
||||
dependencies = [
|
||||
"oxide_bytecode",
|
||||
"oxide_compiler",
|
||||
@@ -1300,7 +1300,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "oxide_fold"
|
||||
version = "0.17.6"
|
||||
version = "0.17.7"
|
||||
dependencies = [
|
||||
"num-bigint",
|
||||
"oxide_bytecode",
|
||||
@@ -1312,7 +1312,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "oxide_ir"
|
||||
version = "0.17.6"
|
||||
version = "0.17.7"
|
||||
dependencies = [
|
||||
"oxide_bytecode",
|
||||
"oxide_log",
|
||||
@@ -1321,7 +1321,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "oxide_kernel"
|
||||
version = "0.17.6"
|
||||
version = "0.17.7"
|
||||
dependencies = [
|
||||
"bumpalo",
|
||||
"dashmap",
|
||||
@@ -1335,7 +1335,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "oxide_liveness"
|
||||
version = "0.17.6"
|
||||
version = "0.17.7"
|
||||
dependencies = [
|
||||
"oxide_bytecode",
|
||||
"oxide_cfg",
|
||||
@@ -1347,7 +1347,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "oxide_log"
|
||||
version = "0.17.6"
|
||||
version = "0.17.7"
|
||||
dependencies = [
|
||||
"tracing",
|
||||
"tracing-appender",
|
||||
@@ -1356,11 +1356,11 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "oxide_net"
|
||||
version = "0.17.6"
|
||||
version = "0.17.7"
|
||||
|
||||
[[package]]
|
||||
name = "oxide_parser"
|
||||
version = "0.17.6"
|
||||
version = "0.17.7"
|
||||
dependencies = [
|
||||
"oxc_allocator",
|
||||
"oxc_ast",
|
||||
@@ -1375,7 +1375,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "oxide_regalloc"
|
||||
version = "0.17.6"
|
||||
version = "0.17.7"
|
||||
dependencies = [
|
||||
"oxide_bytecode",
|
||||
"oxide_cfg",
|
||||
@@ -1391,7 +1391,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "oxide_runtime_api"
|
||||
version = "0.17.6"
|
||||
version = "0.17.7"
|
||||
dependencies = [
|
||||
"num-bigint",
|
||||
"num-traits",
|
||||
@@ -1404,7 +1404,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "oxide_test262"
|
||||
version = "0.17.6"
|
||||
version = "0.17.7"
|
||||
dependencies = [
|
||||
"oxide_bytecode",
|
||||
"oxide_compiler",
|
||||
@@ -1424,7 +1424,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "oxide_types"
|
||||
version = "0.17.6"
|
||||
version = "0.17.7"
|
||||
dependencies = [
|
||||
"bumpalo",
|
||||
"dashmap",
|
||||
@@ -1437,7 +1437,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "oxide_vm"
|
||||
version = "0.17.6"
|
||||
version = "0.17.7"
|
||||
dependencies = [
|
||||
"bumpalo",
|
||||
"chrono",
|
||||
|
||||
+1
-1
@@ -25,7 +25,7 @@ members = [
|
||||
]
|
||||
|
||||
[workspace.package]
|
||||
version = "0.17.6"
|
||||
version = "0.17.7"
|
||||
edition = "2021"
|
||||
license = "MIT"
|
||||
rust-version = "1.80"
|
||||
|
||||
@@ -140,11 +140,15 @@ impl SuspendedFrame {
|
||||
} else {
|
||||
vm.current_gen
|
||||
};
|
||||
let table = vm.tables.get(&gen);
|
||||
let module = table.and_then(|t| t.modules.get(self.sub_idx as usize));
|
||||
let constants = module.map(|m| m.constants.clone());
|
||||
match constants {
|
||||
Some(c) => vm.activate_immutables(gen, self.sub_idx as usize, &c),
|
||||
// 平表条目取 Arc 克隆(与调用方共享同一编译产物):后续 activate_immutables
|
||||
// 等 &mut 调用期间不持有对注册表的借用。
|
||||
let module = vm
|
||||
.tables
|
||||
.get(&gen)
|
||||
.and_then(|t| t.modules.get(self.sub_idx as usize))
|
||||
.cloned();
|
||||
match module {
|
||||
Some(m) => vm.activate_immutables(gen, self.sub_idx as usize, &m),
|
||||
None => return Err("suspended state module table is no longer available".into()),
|
||||
}
|
||||
// 模板缓存键维度随模块恢复:与 activate_immutables 同位还原。
|
||||
|
||||
@@ -393,7 +393,7 @@ impl Vm {
|
||||
|
||||
self.pc = 0;
|
||||
self.bytecode = sub_bytecode;
|
||||
self.activate_immutables(gen, sub_idx, &sub.constants);
|
||||
self.activate_immutables(gen, sub_idx, &sub);
|
||||
self.cell_stack.push(Vec::with_capacity(sub.cells_needed as usize));
|
||||
self.reload_builtin_mirror_slots(&sub.builtin_reg_map, 0);
|
||||
|
||||
|
||||
@@ -61,15 +61,12 @@ impl Vm {
|
||||
for value in &self.regs {
|
||||
f(RootGroup::Regs, *value);
|
||||
}
|
||||
// 各代际 immutables 缓存含 session BigInt(new_bigint 分配),未入根则被
|
||||
// sweep 释放 → 常量池加载时悬垂。perm 字符串无害(不在 session 集合中)。
|
||||
for table in self.tables.values() {
|
||||
for once_lock in &table.immutables {
|
||||
if let Some(immutable_vec) = once_lock.get() {
|
||||
for &value in immutable_vec.iter() {
|
||||
f(RootGroup::Immutables, value);
|
||||
}
|
||||
}
|
||||
// 模块级装载缓存的常量池含 BigInt(条目自持存储,不在 session_bigint_ptrs
|
||||
// 内):未入根则逐组计数失真(BigInt 不在串表内、sweep 不触达,无悬垂)。
|
||||
// perm 字符串无害(不在 session 集合中)。
|
||||
for entry in self.module_cache.values() {
|
||||
for &value in entry.immutables.iter() {
|
||||
f(RootGroup::Immutables, value);
|
||||
}
|
||||
}
|
||||
for frame in &self.frames {
|
||||
@@ -265,6 +262,9 @@ impl Vm {
|
||||
/// O(1) 读,供单 run 分配上限判定。收集释放后读数回落存活集量级;
|
||||
/// 轻量 reset 保留账目与表(仅复位峰值与水位),full_reset 清零重起算。
|
||||
///
|
||||
/// 注意:BigInt 表长分量不含模块级装载缓存的常量 BigInt(条目自持存储,
|
||||
/// 一次性装载成本、非 run 内分配),单 run 分配上限语义为此略放宽,可接受。
|
||||
///
|
||||
/// 注意:手工堆账目只在对象分配/字符串分配/GC 回收点更新,执行期对象
|
||||
/// 属性区(元素/属性向量扩容)增长对其不可见——上限判定须配合
|
||||
/// [`Self::run_alloc_bytes_full`] 的深采样层。
|
||||
@@ -286,7 +286,8 @@ impl Vm {
|
||||
/// 存活对象头加堆数据(属性/元素/meta 向量容量、upvalue 列表容量、native
|
||||
/// 状态盒)+ 存活串(头加 payload)+ BigInt 表长 × 尺寸 + cell 表长 ×
|
||||
/// 尺寸。重算即属性区扩容等账目盲区的兜底,读数为当前真实总量,零增长时
|
||||
/// 与 [`Self::run_alloc_bytes`] 的轻层公式逐位相等。
|
||||
/// 与 [`Self::run_alloc_bytes`] 的轻层公式逐位相等。BigInt 表长分量同
|
||||
/// 轻层口径:不含模块级装载缓存的常量 BigInt(条目自持存储)。
|
||||
pub(crate) fn run_alloc_bytes_full(&self) -> u64 {
|
||||
let mut bytes = 0u64;
|
||||
for &ptr in self.realm.gc.borrow().session_object_ptrs.iter() {
|
||||
|
||||
@@ -9,7 +9,6 @@ use std::sync::Arc;
|
||||
#[cfg(test)]
|
||||
use std::sync::OnceLock;
|
||||
|
||||
#[cfg(test)]
|
||||
use oxide_bytecode::module::CompiledModule;
|
||||
use oxide_bytecode::opcode;
|
||||
use smallvec::SmallVec;
|
||||
@@ -185,6 +184,27 @@ pub(crate) enum DisposeResumeKind {
|
||||
Escape(Completion),
|
||||
}
|
||||
|
||||
/// 模块级装载缓存条目:常量池与 si 侧表各存一份,BigInt 存储随条目自持。
|
||||
///
|
||||
/// 生命周期与 Vm 同消亡(跨 `full_reset` 存活):常量 BigInt 不在
|
||||
/// `session_bigint_ptrs` 内,session 拆除不悬垂;`Drop for Vm` 随条目 Vec 整体
|
||||
/// 释放,无手工 `Box::from_raw`、无双放。
|
||||
pub(crate) struct ModuleLoadEntry {
|
||||
/// 保持模块 Arc 存活:键指针在条目存活期内恒有效,杜绝分配器复用旧指针
|
||||
/// 撞出新模块的假命中。不得为省一个 Arc 引用而改为裸指针键。
|
||||
/// 只保活不读值。
|
||||
#[allow(dead_code)]
|
||||
pub(crate) module: Arc<CompiledModule>,
|
||||
/// 已转换常量池(BigInt 条目指针指向 bigint_storage 内对应元素)。
|
||||
pub(crate) immutables: Arc<Vec<JsValue>>,
|
||||
/// BigInt 存储:immutables 中 BigInt 指针的宿主(下标一一对应),
|
||||
/// 由缓存条目独占,随 Vm 消亡。
|
||||
#[allow(dead_code)]
|
||||
pub(crate) bigint_storage: Arc<Vec<num_bigint::BigInt>>,
|
||||
/// NEW_OBJECT 静态键 si 侧表(纯 u32,无 GC 边)。
|
||||
pub(crate) si_table: Arc<Vec<Option<u32>>>,
|
||||
}
|
||||
|
||||
/// 基于寄存器的 JS 虚拟机:持有执行状态、寄存器文件、调用栈与 session 内存。
|
||||
///
|
||||
/// 执行入口为 [`Vm::run`](见 `vm_runtime` 模块);内存模型为统一入口分配 +
|
||||
@@ -198,8 +218,9 @@ pub struct Vm {
|
||||
/// 零拷贝),子模块平表源保持惰性 COW,IC 写回经 `bytecode_mut` 的
|
||||
/// `Arc::make_mut` 写时复制,保证独占后才改写(miss 时才深拷贝,频率低)。
|
||||
pub(crate) bytecode: Arc<[opcode::Instr]>,
|
||||
/// 当前活动模块已转换不可变常量的只读视图(指向当前表代际的 immutables 内部)。
|
||||
/// 用胖 `*const`:常量 Vec 归表代际所有且 OnceLock 只填一次(堆分配地址稳定)。
|
||||
/// 当前活动模块已转换不可变常量的只读视图(指向模块级装载缓存条目的
|
||||
/// 常量 Vec 内部)。用胖 `*const`:常量 Vec 归缓存条目所有(随 Vm 同消亡、
|
||||
/// 跨 full_reset 存活)且只填充一次(堆分配地址稳定)。
|
||||
pub(crate) active_immutables: *const [JsValue],
|
||||
pub(crate) frames: SmallVec<[CallFrame; 16]>,
|
||||
/// 帧身份单调计数器:压帧时分配 `CallFrame.frame_id`(mapped arguments 状态盒
|
||||
@@ -247,6 +268,13 @@ pub struct Vm {
|
||||
/// 存活函数对象引用的代际(`reclaim_unreferenced_tables`),内存有界于
|
||||
/// 存活函数对象数,不随 run 数单调增。
|
||||
pub(crate) tables: HashMap<u32, Box<TableGen>>,
|
||||
/// 模块级装载缓存:键 = 编译模块 Arc 指针身份,条目自持模块 Arc 防指针
|
||||
/// 复用撞键。命中时零转换、零字节码扫描、零 BigInt 分配;未命中时转换一次
|
||||
/// 并填缓存。生命周期与 Vm 同消亡:`full_reset` 的 `teardown_session_heap_data`
|
||||
/// 不触碰条目 BigInt 存储(不在 `session_bigint_ptrs` 内),拆除不悬垂。
|
||||
/// 条目数有界于该 Vm 跑过的不同模块数(CodeForge 容量加动态编译面),
|
||||
/// 不随 run 数增长。
|
||||
pub(crate) module_cache: HashMap<*const CompiledModule, ModuleLoadEntry>,
|
||||
/// 当前 run 正在装载的表代际(构造器预登记 gen 0 空表占位,run() 换表时 +1)。
|
||||
pub(crate) current_gen: u32,
|
||||
/// 帧切换时暂存调用方字节码的 Arc 栈(与 `bytecode` 同共享语义)。
|
||||
@@ -495,6 +523,11 @@ impl Drop for Vm {
|
||||
// 6. worker_objects 字段(HashMap<u64, JsValue>)持本 Vm session 堆的 Worker
|
||||
// 对象指针,归不变量 1 覆盖:对象由本 Vm 独占分配、地址稳定、随 Vm 整体
|
||||
// 迁移,跨线程移动不悬垂。
|
||||
//
|
||||
// 7. module_cache 字段(HashMap<*const CompiledModule, ModuleLoadEntry>):
|
||||
// 条目自持模块 Arc 与常量 BigInt 存储(Vec<BigInt>),存储随 Vm 整体迁移
|
||||
// (同不变量 1 条款:Vm 独占分配、地址稳定、跨线程移动不悬垂);键是
|
||||
// Arc 指针身份,条目自持 Arc 保证键指针在条目存活期内不被分配器复用。
|
||||
unsafe impl Send for Vm {}
|
||||
|
||||
impl Vm {
|
||||
|
||||
@@ -3,17 +3,18 @@
|
||||
|
||||
use std::sync::{Arc, OnceLock};
|
||||
|
||||
use oxide_bytecode::module::{CompiledModule, Constant};
|
||||
use oxide_bytecode::module::CompiledModule;
|
||||
use oxide_bytecode::opcode;
|
||||
use oxide_types::object::JsObject;
|
||||
use oxide_types::value::JsValue;
|
||||
|
||||
use super::Vm;
|
||||
|
||||
/// 单代子模块平表:模块平表 + 每模块不可变常量缓存,注册进 `Vm::tables` 按代际管理。
|
||||
/// 单代子模块平表:模块平表 + 每模块不可变常量缓存槽位,注册进 `Vm::tables`
|
||||
/// 按代际管理。
|
||||
///
|
||||
/// 以 `Box` 承载(HashMap 扩容搬 Box 指针不搬被指物):`active_immutables` 与
|
||||
/// `saved_immutables_stack` 是伸入 immutables 内部常量 Vec 的胖指针,表地址须
|
||||
/// `saved_immutables_stack` 是伸入模块级装载缓存条目常量 Vec 的胖指针,表地址须
|
||||
/// 在其整个生命周期内稳定。动态扩表(`create_dynamic_function` /
|
||||
/// `create_dynamic_script`)对 modules 外层 Arc 做 `make_mut`:彼时该 Arc 的
|
||||
/// 强引用唯一持有者是本表(函数对象只持 u32 下标不持 Arc),原地扩展不分叉。
|
||||
@@ -21,15 +22,17 @@ pub(crate) struct TableGen {
|
||||
/// 全局扁平模块表:下标 = 模块 `flat_id`(顶层 0,子模块 flatten 后全局唯一)。
|
||||
/// 函数对象 `sub_module_index` 即该平表下标,逃逸函数也能自足解析。
|
||||
pub(crate) modules: Arc<Vec<Arc<CompiledModule>>>,
|
||||
/// 每次 run 转换一次的不可变常量缓存:下标 0 = 顶层模块,sub_idx = modules[sub_idx]。
|
||||
/// 每个 `OnceLock` 保存该模块常量本次运行中只转换一次的 `JsValue` 结果。
|
||||
/// 不可变常量是标量 + perm 字符串,只读,GC 根收集按表代际遍历。
|
||||
pub(crate) immutables: Vec<OnceLock<Vec<JsValue>>>,
|
||||
/// NEW_OBJECT 静态键 si 侧表,与 `immutables` 平行(下标 = flat_id,内部下标 =
|
||||
/// 常量池下标):`Some(si)` 是模块装载期预 intern 的键 si,`None` 表示该常量
|
||||
/// 不是静态键。填充与 `immutables` 激活同点(`activate_immutables`),构造期
|
||||
/// 直读免逐构造键推导。
|
||||
pub(crate) si_tables: Vec<OnceLock<Vec<Option<u32>>>>,
|
||||
/// 不可变常量缓存槽位:下标 0 = 顶层模块,sub_idx = modules[sub_idx]。
|
||||
/// 每个 `OnceLock` 克隆模块级装载缓存条目(`Vm::module_cache`)中该模块
|
||||
/// 只转换一次的 `JsValue` 结果 Arc 入槽;槽位只作代际视图,转换本体归
|
||||
/// 缓存条目所有(跨 `full_reset` 存活)。不可变常量是标量 + perm 字符串 +
|
||||
/// 条目自持 BigInt,只读,GC 根收集按缓存条目遍历。
|
||||
pub(crate) immutables: Vec<OnceLock<Arc<Vec<JsValue>>>>,
|
||||
/// NEW_OBJECT 静态键 si 侧表槽位,与 `immutables` 平行(下标 = flat_id,
|
||||
/// 内部下标 = 常量池下标):`Some(si)` 是模块装载期预 intern 的键 si,
|
||||
/// `None` 表示该常量不是静态键。填充与 `immutables` 激活同点
|
||||
/// (`activate_immutables`),构造期直读免逐构造键推导。
|
||||
pub(crate) si_tables: Vec<OnceLock<Arc<Vec<Option<u32>>>>>,
|
||||
}
|
||||
|
||||
impl Vm {
|
||||
@@ -39,9 +42,9 @@ impl Vm {
|
||||
if self.active_immutables.is_null() {
|
||||
&[]
|
||||
} else {
|
||||
// SAFETY: active_immutables 指向某表代际 immutables 内 OnceLock 的常量
|
||||
// Vec,Vec 只填充一次且堆地址稳定;所指代际表在注册表内存活(当前
|
||||
// 代际恒在,旧代际由存活函数对象引用保活)。
|
||||
// SAFETY: active_immutables 指向模块级装载缓存条目(module_cache)的
|
||||
// 常量 Vec,条目随 Vm 同消亡(跨 full_reset 存活),Vec 只填充一次且
|
||||
// 堆地址稳定。
|
||||
unsafe { &*self.active_immutables }
|
||||
}
|
||||
}
|
||||
@@ -94,26 +97,43 @@ impl Vm {
|
||||
self.tables.len()
|
||||
}
|
||||
|
||||
/// 激活代际 `gen` 平表中模块 `cache_idx` 的不可变常量,只转换一次存入该代际
|
||||
/// `immutables[cache_idx]`,并把 `active_immutables` 指向该 Vec。
|
||||
/// `constants` 由调用方传入(它已持有 `&module.constants`)。
|
||||
/// 激活代际 `gen` 平表中模块 `cache_idx` 的不可变常量,并把
|
||||
/// `active_immutables` 指向该模块已转换常量池。
|
||||
///
|
||||
/// 转换本体归模块级装载缓存(`Vm::module_cache`,以模块 Arc 指针身份为键)
|
||||
/// 所有:命中时零转换、零字节码扫描、零 BigInt 分配;未命中时
|
||||
/// `convert_module_constants` 转换一次并填缓存。本方法只把条目 Arc 克隆入
|
||||
/// 该代际槽位(O(1)),槽位随代际回收,条目随 Vm 消亡。
|
||||
///
|
||||
/// # 边界与前提
|
||||
/// - `gen` 的表须已在注册表中(当前代际由 run() 登记,旧代际由存活函数
|
||||
/// 对象引用保活);`cache_idx` 须小于该代际平表长度。
|
||||
pub(crate) fn activate_immutables(&mut self, gen: u32, cache_idx: usize, constants: &[Constant]) {
|
||||
// 用裸指针访问缓存槽,避免 get_or_init(借用表 immutables)与 &self 的
|
||||
// convert_immutables 闭包和随后对 active_immutables 的写入发生借用冲突。
|
||||
// 成立前提:代际表归注册表所有、只读、Box 承载地址稳定。
|
||||
/// - `module` 是平表条目同一 Arc 实例(调用方克隆),作缓存键。
|
||||
pub(crate) fn activate_immutables(&mut self, gen: u32, cache_idx: usize, module: &Arc<CompiledModule>) {
|
||||
// 用裸指针访问缓存槽,避免 get_or_init(借用表 immutables)与查/建
|
||||
// module_cache 的借用发生冲突。成立前提:代际表归注册表所有、只读、
|
||||
// Box 承载地址稳定。
|
||||
let table = self.tables.get_mut(&gen).expect("激活的代际表须在注册表中");
|
||||
let slot: *const OnceLock<Vec<JsValue>> = &table.immutables[cache_idx];
|
||||
// si 侧表与 immutables 同点填充:模块字节码从平表取(同裸指针手法,
|
||||
// 平表 Arc 归注册表所有、地址稳定),预 intern 静态键 si。
|
||||
let si_slot: *const OnceLock<Vec<Option<u32>>> = &table.si_tables[cache_idx];
|
||||
let bc: *const [opcode::Instr] = &table.modules[cache_idx].bytecode[..];
|
||||
let vec = unsafe { &*slot }.get_or_init(|| self.convert_immutables(constants));
|
||||
unsafe { &*si_slot }.get_or_init(|| self.fill_si_table(vec, unsafe { &*bc }));
|
||||
self.active_immutables = vec.as_slice() as *const [JsValue];
|
||||
let slot: *const OnceLock<Arc<Vec<JsValue>>> = &table.immutables[cache_idx];
|
||||
// si 侧表槽位与 immutables 同点填充(条目共享同一份 si 表 Arc)。
|
||||
let si_slot: *const OnceLock<Arc<Vec<Option<u32>>>> = &table.si_tables[cache_idx];
|
||||
let key = Arc::as_ptr(module);
|
||||
let entry = match self.module_cache.get(&key) {
|
||||
Some(e) => e,
|
||||
None => {
|
||||
let (immutables, bigint_storage, si_table) = self.convert_module_constants(module);
|
||||
let new_entry = super::ModuleLoadEntry {
|
||||
module: Arc::clone(module),
|
||||
immutables,
|
||||
bigint_storage,
|
||||
si_table,
|
||||
};
|
||||
self.module_cache.entry(key).or_insert(new_entry)
|
||||
}
|
||||
};
|
||||
unsafe { &*slot }.get_or_init(|| Arc::clone(&entry.immutables));
|
||||
unsafe { &*si_slot }.get_or_init(|| Arc::clone(&entry.si_table));
|
||||
self.active_immutables = entry.immutables.as_slice() as *const [JsValue];
|
||||
}
|
||||
|
||||
/// 填充模块的 NEW_OBJECT 静态键 si 侧表:按 dispatch 主循环同口径
|
||||
@@ -124,7 +144,7 @@ impl Vm {
|
||||
/// # 边界与前提
|
||||
/// - 键值取自已转换的不可变常量(`values`),与构造期读同一池。
|
||||
/// - 键推导失败(非字符串键的防御面)保持 None,构造期回退旧路径同口径重推。
|
||||
fn fill_si_table(&mut self, values: &[JsValue], bytecode: &[opcode::Instr]) -> Vec<Option<u32>> {
|
||||
pub(crate) fn fill_si_table(&mut self, values: &[JsValue], bytecode: &[opcode::Instr]) -> Vec<Option<u32>> {
|
||||
let mut si = vec![None; values.len()];
|
||||
let mut i = 0;
|
||||
while i < bytecode.len() {
|
||||
|
||||
@@ -476,7 +476,7 @@ impl Vm {
|
||||
}
|
||||
self.pc = 0;
|
||||
self.bytecode = Arc::clone(&sub.bytecode);
|
||||
self.activate_immutables(gen, sub_idx, &sub.constants);
|
||||
self.activate_immutables(gen, sub_idx, &sub);
|
||||
self.active_flat_id = sub_idx as u32;
|
||||
self.active_table_gen = gen;
|
||||
self.active_reg_limit = sub.n_registers.max(1);
|
||||
@@ -726,7 +726,7 @@ impl Vm {
|
||||
} else {
|
||||
self.bytecode = Arc::clone(&module.bytecode);
|
||||
}
|
||||
self.activate_immutables(self.current_gen, 0, &module.constants);
|
||||
self.activate_immutables(self.current_gen, 0, module);
|
||||
self.root_reg_limit = module.n_registers.max(1);
|
||||
self.active_reg_limit = self.root_reg_limit;
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@ use std::cell::RefCell;
|
||||
use std::collections::VecDeque;
|
||||
use std::sync::Arc;
|
||||
|
||||
use oxide_bytecode::module::Constant;
|
||||
use oxide_bytecode::module::{CompiledModule, Constant};
|
||||
|
||||
use crate::bindings;
|
||||
use crate::realm::Realm;
|
||||
@@ -76,6 +76,10 @@ impl FunctionFlags {
|
||||
}
|
||||
}
|
||||
|
||||
/// 模块级装载缓存条目三件套:已转换常量池、BigInt 自持存储、si 侧表。
|
||||
pub(crate) type ModuleLoadTriple =
|
||||
(Arc<Vec<JsValue>>, Arc<Vec<num_bigint::BigInt>>, Arc<Vec<Option<u32>>>);
|
||||
|
||||
impl Vm {
|
||||
/// Cons(rope)节点保留的单元阈值:拼接总长(UTF-16 单元数)≤ 该值时
|
||||
/// 急切扁平。小链的"链接 + 首次消费扁平化"双重分配高于直接拷贝,直接
|
||||
@@ -177,6 +181,7 @@ impl Vm {
|
||||
si_tables: Vec::new(),
|
||||
}),
|
||||
)]),
|
||||
module_cache: std::collections::HashMap::new(),
|
||||
current_gen: 0,
|
||||
saved_bytecode_stack: Vec::new(),
|
||||
saved_immutables_stack: Vec::new(),
|
||||
@@ -344,6 +349,7 @@ impl Vm {
|
||||
si_tables: Vec::new(),
|
||||
}),
|
||||
)]),
|
||||
module_cache: std::collections::HashMap::new(),
|
||||
current_gen: 0,
|
||||
saved_bytecode_stack: Vec::new(),
|
||||
saved_immutables_stack: Vec::new(),
|
||||
@@ -524,6 +530,9 @@ impl Vm {
|
||||
self.bytecode = Arc::default();
|
||||
// 注册表随 session 重建整体清空:session 对象(唯一按代际保活表者)全部
|
||||
// 消失,残留表无引用源。回到构造期同态:gen 0 空表占位 + current_gen = 0。
|
||||
// 模块级装载缓存(module_cache)此处**不得**清空:条目 BigInt 存储归条目
|
||||
// 自持(不在 session_bigint_ptrs 内),session 拆除不悬垂;误清则收益归零
|
||||
// (同模块跨 run 重装退化回每 run 全量重转换),无正确性错误。
|
||||
self.tables.clear();
|
||||
self.current_gen = 0;
|
||||
self.tables.insert(
|
||||
@@ -922,7 +931,8 @@ impl Vm {
|
||||
/// 分配一个 BigInt 值:把 `i128` 堆分配为 box 并返回携带指针的 `JsValue`。
|
||||
///
|
||||
/// box 指针登记进 `realm.gc.session_bigint_ptrs`,在 `full_reset` 统一释放。
|
||||
/// `&self` 使 `convert_immutables`(常量池 → JsValue)也能分配。
|
||||
/// `&self` 供运行时面调用(`BigInt(42)` 字面量、算术结果等);模块常量池
|
||||
/// BigInt 走模块级装载缓存的条目自持存储,不经本入口。
|
||||
pub fn new_bigint(&self, v: num_bigint::BigInt) -> JsValue {
|
||||
let ptr = Box::into_raw(Box::new(v));
|
||||
self.realm.gc.borrow_mut().session_bigint_ptrs.borrow_mut().push(ptr);
|
||||
@@ -935,23 +945,51 @@ impl Vm {
|
||||
unsafe { &*val.as_bigint_ptr() }
|
||||
}
|
||||
|
||||
fn convert_constant(&self, constant: &Constant) -> JsValue {
|
||||
match constant {
|
||||
Constant::Number(v) => JsValue::float(*v),
|
||||
Constant::Int(v) => JsValue::int(*v),
|
||||
Constant::BigInt(v) => self.new_bigint(v.clone()),
|
||||
Constant::String(s) => self.perm_string(s),
|
||||
Constant::Boolean(b) => JsValue::bool(*b),
|
||||
Constant::Null => JsValue::null(),
|
||||
Constant::Undefined => JsValue::undefined(),
|
||||
/// 把模块常量池转换为模块级装载缓存条目三件套:已转换常量池、
|
||||
/// BigInt 自持存储、si 侧表。不可能失败——CreateClosure/CreateRegExp
|
||||
/// 已把函数与正则移出常量池,池中只剩标量 + 永久 intern 字符串值。
|
||||
///
|
||||
/// # 步骤
|
||||
/// 1. 按常量池 BigInt 计数精确预分配自持存储(`push` 后取地址作指针,
|
||||
/// 存储扩容即指针悬垂)。
|
||||
/// 2. 逐常量转换:标量臂与既有口径一致;BigInt 条目入自持存储,
|
||||
/// 指针指向存储内对应元素。
|
||||
/// 3. 经 `fill_si_table` 同口径填充 si 侧表。
|
||||
///
|
||||
/// # 注意事项
|
||||
/// - BigInt 常量只写条目自持存储,不登记 `session_bigint_ptrs`:
|
||||
/// `new_bigint` 保持运行时专属(`BigInt(42)` 字面量、算术结果等),
|
||||
/// 两表 box 生命周期不相交,full_reset 拆除与条目 Drop 各释各的,无双放。
|
||||
pub(crate) fn convert_module_constants(&mut self, module: &CompiledModule) -> ModuleLoadTriple {
|
||||
let bigint_count = module
|
||||
.constants
|
||||
.iter()
|
||||
.filter(|c| matches!(c, Constant::BigInt(_)))
|
||||
.count();
|
||||
let mut bigint_storage = Vec::with_capacity(bigint_count);
|
||||
let mut values = Vec::with_capacity(module.constants.len());
|
||||
for constant in &module.constants {
|
||||
match constant {
|
||||
Constant::Number(v) => values.push(JsValue::float(*v)),
|
||||
Constant::Int(v) => values.push(JsValue::int(*v)),
|
||||
Constant::BigInt(v) => {
|
||||
bigint_storage.push(v.clone());
|
||||
// 存储按计数精确预分配、循环内不再扩容:push 后取地址,
|
||||
// 指针在条目存活期内恒有效。
|
||||
// SAFETY: 存储未扩容,as_mut_ptr 指向的缓冲在条目存活期内
|
||||
// 不被释放也不被搬移,加偏移取的是刚 push 的元素。
|
||||
let ptr = unsafe { bigint_storage.as_mut_ptr().add(bigint_storage.len() - 1) };
|
||||
values.push(JsValue::bigint(ptr));
|
||||
}
|
||||
Constant::String(s) => values.push(self.perm_string(s)),
|
||||
Constant::Boolean(b) => values.push(JsValue::bool(*b)),
|
||||
Constant::Null => values.push(JsValue::null()),
|
||||
Constant::Undefined => values.push(JsValue::undefined()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// 把模块的不可变常量池转换为 `JsValue`。不可能失败——CreateClosure/CreateRegExp
|
||||
/// 已把函数与正则移出常量池,池中只剩标量 + 永久 intern 字符串值。`&self`
|
||||
/// 使其可运行于 `OnceLock::get_or_init` 内部。
|
||||
pub(crate) fn convert_immutables(&self, constants: &[Constant]) -> Vec<JsValue> {
|
||||
constants.iter().map(|c| self.convert_constant(c)).collect()
|
||||
let immutables = Arc::new(values);
|
||||
let si_table = Arc::new(self.fill_si_table(&immutables, &module.bytecode));
|
||||
(immutables, Arc::new(bigint_storage), si_table)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -498,6 +498,51 @@ fn immutables_filled_once_per_module() {
|
||||
// 子模块常量改走 temp_immutables(避免缓存下标冲突)
|
||||
}
|
||||
|
||||
/// 模块级装载缓存:同模块跨 run(无 full_reset)常量转换只发生一次——
|
||||
/// 常量 BigInt 入条目自持存储、不入 session 表,session 表长不随 run 数增长,
|
||||
/// 缓存条目不重复创建。
|
||||
#[test]
|
||||
fn module_cache_converts_constants_once_across_runs() {
|
||||
let mut vm = Vm::new();
|
||||
let module = compile_module("var x = 10n; x");
|
||||
vm.run(&module).expect("run1");
|
||||
// 常量 BigInt 走条目自持存储,session 表不留痕。
|
||||
assert!(
|
||||
vm.realm.gc.borrow().session_bigint_ptrs.borrow().is_empty(),
|
||||
"常量 BigInt 不得登记 session 表"
|
||||
);
|
||||
vm.run(&module).expect("run2");
|
||||
// 第二次 run 命中缓存:不重转换、不重登记,表长不增、条目不增。
|
||||
assert!(
|
||||
vm.realm.gc.borrow().session_bigint_ptrs.borrow().is_empty(),
|
||||
"同模块跨 run 不得重复登记常量 BigInt 入 session 表"
|
||||
);
|
||||
assert_eq!(vm.module_cache.len(), 1, "同模块跨 run 不得新建缓存条目");
|
||||
}
|
||||
|
||||
/// 模块级装载缓存跨 full_reset 存活:条目自持 BigInt 存储不在
|
||||
/// session_bigint_ptrs 内,session 拆除后指针不悬垂,常量值仍可读。
|
||||
#[test]
|
||||
fn module_cache_bigint_readable_after_full_reset() {
|
||||
let mut vm = Vm::new();
|
||||
let module = compile_module("var x = 10n; x");
|
||||
let result = vm.run(&module).expect("run");
|
||||
assert!(result.is_bigint());
|
||||
vm.full_reset();
|
||||
// full_reset 不清装载缓存:条目 BigInt 存储归条目自持,拆除不悬垂。
|
||||
let entry = vm
|
||||
.module_cache
|
||||
.get(&Arc::as_ptr(&module))
|
||||
.expect("full_reset 不得清装载缓存");
|
||||
let v = entry
|
||||
.immutables
|
||||
.iter()
|
||||
.find(|v| v.is_bigint())
|
||||
.copied()
|
||||
.expect("常量池含 BigInt 常量");
|
||||
assert_eq!(vm.bigint_value(v), &num_bigint::BigInt::from(10));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn dynamic_function_basic_arity() {
|
||||
let mut vm = Vm::new();
|
||||
|
||||
Reference in New Issue
Block a user