perf: CONCAT_N 多操作数拼接单趟精确预分配替代逐次 ADD
新增 CONCAT_N opcode(0x93):连续加号左结合链摊平为单条指令, 字符串模式单趟精确总长预分配,消除 N-2 次中间串分配与逐次整串重拷贝。 - emit 只下钻 left 链、right 括号不拆(a+(b+c) 退化 ADD),求值序与 f64 结合性保持左结合原义;摊平上限 14 防超长链寄存器压力 - dispatch 两阶段:数值前缀逐项复用 ADD 判定序列(int 溢出升 double、 BigInt 混合 TypeError、对象 ToPrimitive 副作用序),遇字符串转入 字符串模式单趟预分配拼接(concat_n_strings) - RegAlloc graph/rewrite 登记 CONCAT_N ext 操作数(漏收集即错值); ic_helper ext_word_count 登记(clear_ic_caches 字节定位) - contract golden 显式 CONCAT_N 分支(规避 canonical_inst _ 兜底)
This commit is contained in:
@@ -2,6 +2,7 @@ use crate::vm::Vm;
|
||||
use crate::vm_trace;
|
||||
use oxide_runtime_api as coercion;
|
||||
use oxide_types::value::JsValue;
|
||||
use smallvec::SmallVec;
|
||||
|
||||
/// 判断 JsValue 是否为 BigInt 且值为 0(num_bigint 零值判断)。
|
||||
fn bigint_is_zero(v: &num_bigint::BigInt) -> bool {
|
||||
@@ -95,6 +96,120 @@ impl Vm {
|
||||
self.new_string_owned(buf)
|
||||
}
|
||||
|
||||
/// CONCAT_N:多操作数拼接(连续 `+` 左结合链摊平)的两阶段 dispatch。
|
||||
///
|
||||
/// # 步骤
|
||||
/// 1. 读 ext 收集全部操作数寄存器(pc 推进约定同 TEMPLATE_STR:主循环已越过指令,
|
||||
/// 此处先读 ext[0]=n 并推进,再逐操作数推进,总推进 n 个扩展字)。
|
||||
/// 2. 阶段 1 数值前缀折叠:逐项复用 ADD 判定序列(int 溢出升 double / double /
|
||||
/// BigInt 混合 TypeError / 对象 ToPrimitive 副作用顺序),与左结合两两 ADD
|
||||
/// 逐点一致;遇字符串即转入阶段 2。
|
||||
/// 3. 阶段 2 字符串模式单趟预分配:剩余操作数按序 coerce 后一次写齐,消除
|
||||
/// N-2 次中间串分配。
|
||||
///
|
||||
/// # 边界与前提
|
||||
/// - emit 层保证 n ≥ 3;此处 n<2 防御退化(返回首操作数)。
|
||||
/// - Symbol 操作数行为与 concat_strings 一致(push_to_string 无 symbol 分支)。
|
||||
///
|
||||
/// # 副作用
|
||||
/// 写 regs[rd];字符串路径新建一个会话字符串。
|
||||
#[inline(never)]
|
||||
pub(crate) fn dispatch_concat_n(&mut self, rd: usize, a: usize) -> Result<(), String> {
|
||||
vm_trace!("CONCAT_N rd={}", rd);
|
||||
let n = self.bytecode[self.pc] as usize;
|
||||
self.pc += 1;
|
||||
let mut ops = SmallVec::<[usize; 8]>::new();
|
||||
ops.push(a);
|
||||
for _ in 0..n.saturating_sub(1) {
|
||||
let reg = (self.bytecode[self.pc] & 0x7FFF_FFFF) as usize;
|
||||
self.pc += 1;
|
||||
ops.push(reg);
|
||||
}
|
||||
if ops.len() < 2 {
|
||||
self.regs[rd] = self.regs[ops[0]];
|
||||
return Ok(());
|
||||
}
|
||||
// ── 阶段 1:数值前缀折叠(与左结合两两 ADD 逐点一致)──
|
||||
let mut acc = self.regs[ops[0]];
|
||||
let mut idx = 1;
|
||||
while idx < ops.len() {
|
||||
let next = self.regs[ops[idx]];
|
||||
if acc.is_int() && next.is_int() {
|
||||
acc = int_add(acc.as_int(), next.as_int());
|
||||
idx += 1;
|
||||
continue;
|
||||
}
|
||||
if acc.is_double() && next.is_double() {
|
||||
acc = JsValue::float(acc.as_double() + next.as_double());
|
||||
idx += 1;
|
||||
continue;
|
||||
}
|
||||
if acc.is_bigint() && next.is_bigint() {
|
||||
acc = self.new_bigint(self.bigint_value(acc) + self.bigint_value(next));
|
||||
idx += 1;
|
||||
continue;
|
||||
}
|
||||
// 快路径失败才 coerce:acc 已原语零副作用;next 对象 ToPrimitive 在此触发,
|
||||
// 顺序与左结合 ADD 一致。
|
||||
let lhs = self.coerce_primitive_bounded(acc, false)?;
|
||||
let rhs = self.coerce_primitive_bounded(next, false)?;
|
||||
if lhs.is_string() || rhs.is_string() {
|
||||
// ── 阶段 2:字符串模式单趟预分配 ──
|
||||
self.regs[rd] = self.concat_n_strings(lhs, rhs, &ops[idx + 1..])?;
|
||||
return Ok(());
|
||||
}
|
||||
if lhs.is_bigint() && rhs.is_bigint() {
|
||||
acc = self.new_bigint(self.bigint_value(lhs) + self.bigint_value(rhs));
|
||||
idx += 1;
|
||||
continue;
|
||||
}
|
||||
if lhs.is_bigint() != rhs.is_bigint() {
|
||||
// 包装对象 coerce 后暴露 BigInt:混合加法必须抛 TypeError(同 ADD 抛点)。
|
||||
return self.raise_type_error("Cannot mix BigInt and other types, use explicit conversions");
|
||||
}
|
||||
let ln = coercion::to_number(lhs);
|
||||
let rn = coercion::to_number(rhs);
|
||||
acc = JsValue::float(ln + rn);
|
||||
idx += 1;
|
||||
}
|
||||
self.regs[rd] = acc;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// 字符串模式单趟预分配拼接:l/r 已 coerce,剩余操作数按序 coerce 后一次写齐。
|
||||
///
|
||||
/// # 步骤
|
||||
/// 1. 剩余操作数按序 coerce(对象 ToPrimitive 副作用顺序与左结合一致,抛错点先于分配)。
|
||||
/// 2. 精确总长:字符串操作数取字节长,非字符串给 32 字节余量(int/double/bool/null/
|
||||
/// undefined 的十进制文本上界;BigInt 超长时 String 自动扩容兜底)。
|
||||
/// 3. 单趟 push_to_string + new_string_owned(零二次拷贝)。
|
||||
///
|
||||
/// # 副作用
|
||||
/// 新建一个会话字符串。
|
||||
fn concat_n_strings(&mut self, lhs: JsValue, rhs: JsValue, rest: &[usize]) -> Result<JsValue, String> {
|
||||
let mut parts = SmallVec::<[JsValue; 8]>::new();
|
||||
parts.push(lhs);
|
||||
parts.push(rhs);
|
||||
for ® in rest {
|
||||
let v = self.regs[reg];
|
||||
let prim = if v.is_object() { self.coerce_primitive_bounded(v, false)? } else { v };
|
||||
parts.push(prim);
|
||||
}
|
||||
let cap = parts.iter().fold(0usize, |acc, p| {
|
||||
acc + if p.is_string() {
|
||||
// SAFETY: p 是字符串值。
|
||||
unsafe { (*p.as_string_ptr()).len() }
|
||||
} else {
|
||||
32
|
||||
}
|
||||
});
|
||||
let mut buf = String::with_capacity(cap);
|
||||
for p in &parts {
|
||||
coercion::push_to_string(*p, &mut buf);
|
||||
}
|
||||
Ok(self.new_string_owned(buf))
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub(crate) fn dispatch_neg(&mut self, rd: usize, a: usize) -> Result<(), String> {
|
||||
vm_trace!("NEG rd={} r{}={:?}", rd, a, self.regs[a]);
|
||||
|
||||
@@ -78,6 +78,8 @@ fn ext_word_count(bytecode: &[Instr], pc: usize) -> usize {
|
||||
let header = bytecode.get(pc + 1).copied().unwrap_or(0);
|
||||
1 + ((header >> 16) & 0xFFFF) as usize
|
||||
}
|
||||
// CONCAT_N:ext[0]=n=操作数总数,ext 字数 = 1+(n-1) = n。
|
||||
OpCode::CONCAT_N => bytecode.get(pc + 1).copied().unwrap_or(0) as usize,
|
||||
OpCode::NEW_OBJECT => opcode::a(bytecode[pc]) as usize,
|
||||
_ => 0,
|
||||
}
|
||||
@@ -287,3 +289,21 @@ fn ic_set_hit_own_poly(obj: &mut JsObject, bytecode: &[Instr], ext_pc: usize, va
|
||||
}
|
||||
false
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn concat_n_ext_word_count_advances_by_n() {
|
||||
// CONCAT_N rd=1, a=2, ext=[3, 5, 9]:ext[0]=n=3,总 ext 字数 = 3
|
||||
let mut bytecode = vec![opcode::encode(OpCode::CONCAT_N, 1, 2, 0), 3, 5, 9];
|
||||
assert_eq!(ext_word_count(&bytecode, 0), 3, "ext 字数 = n");
|
||||
|
||||
// clear_ic_caches 跳过 CONCAT_N 扩展字,不误当 IC 字清零
|
||||
bytecode.push(opcode::encode(OpCode::NOP, 0, 0, 0));
|
||||
clear_ic_caches(&mut bytecode);
|
||||
assert_eq!(bytecode[1..4], [3, 5, 9], "CONCAT_N ext 字保持原值");
|
||||
assert_eq!(bytecode.len(), 5, "CONCAT_N 指令 + 3 ext + NOP");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1342,6 +1342,10 @@ impl Vm {
|
||||
self.dispatch_add(rd, a, b)?;
|
||||
}
|
||||
|
||||
OpCode::CONCAT_N => {
|
||||
self.dispatch_concat_n(rd, a)?;
|
||||
}
|
||||
|
||||
OpCode::SUB => {
|
||||
binary_arith!(self, a, b, rd, -, false);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user