mirror of
https://github.com/pjh456/OxideJS.git
synced 2026-10-23 18:21:13 +08:00
perf: 常量字符串键计算成员折叠走 IC 静态路径
This commit is contained in:
@@ -136,80 +136,68 @@ impl Emitter {
|
||||
Ok(val_reg)
|
||||
}
|
||||
} else if let oxide_parser::AssignmentTarget::ComputedMemberExpression(member) = &assign.left {
|
||||
if let Some(logical_op) = assign.operator.to_logical_operator() {
|
||||
let store_label = ctx.next_label_id();
|
||||
let end_label = ctx.next_label_id();
|
||||
// 常量字符串键折叠为 IC 静态路径,与 StaticMemberExpression 分支同构。
|
||||
if let Some(key) = crate::expr::member::computed_const_key(&member.expression) {
|
||||
if let Some(logical_op) = assign.operator.to_logical_operator() {
|
||||
let store_label = ctx.next_label_id();
|
||||
let end_label = ctx.next_label_id();
|
||||
let obj_reg = self.emit_expression(&member.object, ctx)?;
|
||||
let idx = ctx.add_constant(Constant::String(key));
|
||||
let key_reg = ctx.alloc_reg();
|
||||
ctx.inst(Inst::load_const(Operand::Reg(key_reg), idx));
|
||||
let result_reg = ctx.alloc_reg();
|
||||
ctx.inst(Inst::new(
|
||||
OpCode::LOAD_VAR,
|
||||
Operand::Reg(result_reg),
|
||||
Operand::Reg(obj_reg),
|
||||
Operand::None,
|
||||
));
|
||||
ctx.inst(Inst::ic_get(Operand::Reg(result_reg), Operand::Reg(key_reg)));
|
||||
self.emit_logical_assign_test(logical_op, result_reg, store_label, end_label, ctx)?;
|
||||
ctx.labels.set_label_pos(store_label, ctx.insts.len());
|
||||
let val_reg = self.emit_expression(&assign.right, ctx)?;
|
||||
ctx.inst(Inst::ic_set(Operand::Reg(obj_reg), Operand::Reg(val_reg), Operand::Reg(key_reg)));
|
||||
ctx.inst(Inst::new(
|
||||
OpCode::LOAD_VAR,
|
||||
Operand::Reg(result_reg),
|
||||
Operand::Reg(val_reg),
|
||||
Operand::None,
|
||||
));
|
||||
ctx.labels.set_label_pos(end_label, ctx.insts.len());
|
||||
return Ok(result_reg);
|
||||
}
|
||||
let obj_reg = self.emit_expression(&member.object, ctx)?;
|
||||
let key_reg = self.emit_expression(&member.expression, ctx)?;
|
||||
let result_reg = ctx.alloc_reg();
|
||||
ctx.inst(Inst::new(
|
||||
OpCode::GET_PROP_DYNAMIC,
|
||||
Operand::Reg(obj_reg),
|
||||
Operand::Reg(key_reg),
|
||||
Operand::Reg(result_reg),
|
||||
));
|
||||
self.emit_logical_assign_test(logical_op, result_reg, store_label, end_label, ctx)?;
|
||||
ctx.labels.set_label_pos(store_label, ctx.insts.len());
|
||||
let val_reg = self.emit_expression(&assign.right, ctx)?;
|
||||
ctx.inst(Inst::new(
|
||||
OpCode::SET_PROP_DYNAMIC,
|
||||
Operand::Reg(obj_reg),
|
||||
Operand::Reg(key_reg),
|
||||
Operand::Reg(val_reg),
|
||||
));
|
||||
ctx.inst(Inst::new(
|
||||
OpCode::LOAD_VAR,
|
||||
Operand::Reg(result_reg),
|
||||
Operand::Reg(val_reg),
|
||||
Operand::None,
|
||||
));
|
||||
ctx.labels.set_label_pos(end_label, ctx.insts.len());
|
||||
return Ok(result_reg);
|
||||
let idx = ctx.add_constant(Constant::String(key));
|
||||
let key_reg = ctx.alloc_reg();
|
||||
ctx.inst(Inst::load_const(Operand::Reg(key_reg), idx));
|
||||
if assign.operator != AssignmentOperator::Assign {
|
||||
let obj = Operand::Reg(obj_reg);
|
||||
let val = Operand::Reg(val_reg);
|
||||
let key = Operand::Reg(key_reg);
|
||||
match assign.operator {
|
||||
AssignmentOperator::Addition => ctx.inst(Inst::compound_member_add(obj, val, key)),
|
||||
AssignmentOperator::Subtraction => ctx.inst(Inst::compound_member_sub(obj, val, key)),
|
||||
AssignmentOperator::Multiplication => ctx.inst(Inst::compound_member_mul(obj, val, key)),
|
||||
AssignmentOperator::Division => ctx.inst(Inst::compound_member_div(obj, val, key)),
|
||||
AssignmentOperator::Remainder => ctx.inst(Inst::compound_member_mod(obj, val, key)),
|
||||
AssignmentOperator::Exponential => ctx.inst(Inst::compound_member_exp(obj, val, key)),
|
||||
AssignmentOperator::BitwiseAnd => ctx.inst(Inst::compound_member_bit_and(obj, val, key)),
|
||||
AssignmentOperator::BitwiseOR => ctx.inst(Inst::compound_member_bit_or(obj, val, key)),
|
||||
AssignmentOperator::BitwiseXOR => ctx.inst(Inst::compound_member_bit_xor(obj, val, key)),
|
||||
AssignmentOperator::ShiftLeft => ctx.inst(Inst::compound_member_shl(obj, val, key)),
|
||||
AssignmentOperator::ShiftRight => ctx.inst(Inst::compound_member_shr(obj, val, key)),
|
||||
AssignmentOperator::ShiftRightZeroFill => ctx.inst(Inst::compound_member_ushr(obj, val, key)),
|
||||
_ => return Err(format!("compound assignment operator {:?} not supported", assign.operator)),
|
||||
}
|
||||
Ok(val_reg)
|
||||
} else {
|
||||
ctx.inst(Inst::ic_set(Operand::Reg(obj_reg), Operand::Reg(val_reg), Operand::Reg(key_reg)));
|
||||
Ok(val_reg)
|
||||
}
|
||||
} else {
|
||||
self.emit_computed_member_dynamic(assign, member, ctx)
|
||||
}
|
||||
let obj_reg = self.emit_expression(&member.object, ctx)?;
|
||||
let key_reg = self.emit_expression(&member.expression, ctx)?;
|
||||
if assign.operator != AssignmentOperator::Assign {
|
||||
// 复合赋值:读属性 → 运算 → 写回。val_reg 同时承载旧值与新值。
|
||||
let val_reg = ctx.alloc_reg();
|
||||
ctx.inst(Inst::new(
|
||||
OpCode::GET_PROP_DYNAMIC,
|
||||
Operand::Reg(obj_reg),
|
||||
Operand::Reg(key_reg),
|
||||
Operand::Reg(val_reg),
|
||||
));
|
||||
let rhs = self.emit_expression(&assign.right, ctx)?;
|
||||
let op = match assign.operator {
|
||||
AssignmentOperator::Addition => OpCode::ADD,
|
||||
AssignmentOperator::Subtraction => OpCode::SUB,
|
||||
AssignmentOperator::Multiplication => OpCode::MUL,
|
||||
AssignmentOperator::Division => OpCode::DIV,
|
||||
AssignmentOperator::Remainder => OpCode::MOD,
|
||||
AssignmentOperator::Exponential => OpCode::COMPOUND_EXP,
|
||||
AssignmentOperator::BitwiseAnd => OpCode::BIT_AND,
|
||||
AssignmentOperator::BitwiseOR => OpCode::BIT_OR,
|
||||
AssignmentOperator::BitwiseXOR => OpCode::BIT_XOR,
|
||||
AssignmentOperator::ShiftLeft => OpCode::SHL,
|
||||
AssignmentOperator::ShiftRight => OpCode::SHR,
|
||||
AssignmentOperator::ShiftRightZeroFill => OpCode::USHR,
|
||||
_ => return Err(format!("compound assignment operator {:?} not supported", assign.operator)),
|
||||
};
|
||||
ctx.inst(Inst::new(op, Operand::Reg(val_reg), Operand::Reg(val_reg), Operand::Reg(rhs)));
|
||||
ctx.inst(Inst::new(
|
||||
OpCode::SET_PROP_DYNAMIC,
|
||||
Operand::Reg(obj_reg),
|
||||
Operand::Reg(key_reg),
|
||||
Operand::Reg(val_reg),
|
||||
));
|
||||
return Ok(val_reg);
|
||||
}
|
||||
let val_reg = self.emit_expression(&assign.right, ctx)?;
|
||||
ctx.inst(Inst::new(
|
||||
OpCode::SET_PROP_DYNAMIC,
|
||||
Operand::Reg(obj_reg),
|
||||
Operand::Reg(key_reg),
|
||||
Operand::Reg(val_reg),
|
||||
));
|
||||
Ok(val_reg)
|
||||
} else if let oxide_parser::AssignmentTarget::PrivateFieldExpression(member) = &assign.left {
|
||||
if assign.operator != AssignmentOperator::Assign {
|
||||
return Err("compound assignment to private fields not supported".into());
|
||||
@@ -421,4 +409,93 @@ impl Emitter {
|
||||
Err("assignment target not supported".into())
|
||||
}
|
||||
}
|
||||
|
||||
/// 计算成员赋值的动态键路径:键运行期求值(非常量键)时经 DYNAMIC 指令读写。
|
||||
///
|
||||
/// # 步骤
|
||||
/// 1. 求值对象与键表达式到寄存器。
|
||||
/// 2. 逻辑赋值:读属性 → 短路测试 → 条件写回;复合赋值:读 → 运算 → 写回;
|
||||
/// 普通赋值直接写回。
|
||||
///
|
||||
/// # 注意事项
|
||||
/// - 键表达式有 ToPropertyKey 副作用或非常量时才调用(常量字符串键走 IC 折叠路径)。
|
||||
fn emit_computed_member_dynamic(
|
||||
&self, assign: &oxide_parser::AssignmentExpression, member: &oxide_parser::ComputedMemberExpression,
|
||||
ctx: &mut CompileCtx,
|
||||
) -> Result<u32, String> {
|
||||
if let Some(logical_op) = assign.operator.to_logical_operator() {
|
||||
let store_label = ctx.next_label_id();
|
||||
let end_label = ctx.next_label_id();
|
||||
let obj_reg = self.emit_expression(&member.object, ctx)?;
|
||||
let key_reg = self.emit_expression(&member.expression, ctx)?;
|
||||
let result_reg = ctx.alloc_reg();
|
||||
ctx.inst(Inst::new(
|
||||
OpCode::GET_PROP_DYNAMIC,
|
||||
Operand::Reg(obj_reg),
|
||||
Operand::Reg(key_reg),
|
||||
Operand::Reg(result_reg),
|
||||
));
|
||||
self.emit_logical_assign_test(logical_op, result_reg, store_label, end_label, ctx)?;
|
||||
ctx.labels.set_label_pos(store_label, ctx.insts.len());
|
||||
let val_reg = self.emit_expression(&assign.right, ctx)?;
|
||||
ctx.inst(Inst::new(
|
||||
OpCode::SET_PROP_DYNAMIC,
|
||||
Operand::Reg(obj_reg),
|
||||
Operand::Reg(key_reg),
|
||||
Operand::Reg(val_reg),
|
||||
));
|
||||
ctx.inst(Inst::new(
|
||||
OpCode::LOAD_VAR,
|
||||
Operand::Reg(result_reg),
|
||||
Operand::Reg(val_reg),
|
||||
Operand::None,
|
||||
));
|
||||
ctx.labels.set_label_pos(end_label, ctx.insts.len());
|
||||
return Ok(result_reg);
|
||||
}
|
||||
let obj_reg = self.emit_expression(&member.object, ctx)?;
|
||||
let key_reg = self.emit_expression(&member.expression, ctx)?;
|
||||
if assign.operator != AssignmentOperator::Assign {
|
||||
// 复合赋值:读属性 → 运算 → 写回。val_reg 同时承载旧值与新值。
|
||||
let val_reg = ctx.alloc_reg();
|
||||
ctx.inst(Inst::new(
|
||||
OpCode::GET_PROP_DYNAMIC,
|
||||
Operand::Reg(obj_reg),
|
||||
Operand::Reg(key_reg),
|
||||
Operand::Reg(val_reg),
|
||||
));
|
||||
let rhs = self.emit_expression(&assign.right, ctx)?;
|
||||
let op = match assign.operator {
|
||||
AssignmentOperator::Addition => OpCode::ADD,
|
||||
AssignmentOperator::Subtraction => OpCode::SUB,
|
||||
AssignmentOperator::Multiplication => OpCode::MUL,
|
||||
AssignmentOperator::Division => OpCode::DIV,
|
||||
AssignmentOperator::Remainder => OpCode::MOD,
|
||||
AssignmentOperator::Exponential => OpCode::COMPOUND_EXP,
|
||||
AssignmentOperator::BitwiseAnd => OpCode::BIT_AND,
|
||||
AssignmentOperator::BitwiseOR => OpCode::BIT_OR,
|
||||
AssignmentOperator::BitwiseXOR => OpCode::BIT_XOR,
|
||||
AssignmentOperator::ShiftLeft => OpCode::SHL,
|
||||
AssignmentOperator::ShiftRight => OpCode::SHR,
|
||||
AssignmentOperator::ShiftRightZeroFill => OpCode::USHR,
|
||||
_ => return Err(format!("compound assignment operator {:?} not supported", assign.operator)),
|
||||
};
|
||||
ctx.inst(Inst::new(op, Operand::Reg(val_reg), Operand::Reg(val_reg), Operand::Reg(rhs)));
|
||||
ctx.inst(Inst::new(
|
||||
OpCode::SET_PROP_DYNAMIC,
|
||||
Operand::Reg(obj_reg),
|
||||
Operand::Reg(key_reg),
|
||||
Operand::Reg(val_reg),
|
||||
));
|
||||
return Ok(val_reg);
|
||||
}
|
||||
let val_reg = self.emit_expression(&assign.right, ctx)?;
|
||||
ctx.inst(Inst::new(
|
||||
OpCode::SET_PROP_DYNAMIC,
|
||||
Operand::Reg(obj_reg),
|
||||
Operand::Reg(key_reg),
|
||||
Operand::Reg(val_reg),
|
||||
));
|
||||
Ok(val_reg)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -89,15 +89,31 @@ impl Emitter {
|
||||
}
|
||||
Expression::ComputedMemberExpression(member) => {
|
||||
let obj_reg = self.emit_expression(&member.object, ctx)?;
|
||||
let key_reg = self.emit_expression(&member.expression, ctx)?;
|
||||
let callee_reg = ctx.alloc_reg();
|
||||
ctx.inst(Inst::new(
|
||||
OpCode::GET_PROP_DYNAMIC,
|
||||
Operand::Reg(obj_reg),
|
||||
Operand::Reg(key_reg),
|
||||
Operand::Reg(callee_reg),
|
||||
));
|
||||
(callee_reg, obj_reg)
|
||||
// 常量字符串键折叠为 IC 静态路径:callee 寄存器独立于对象,this 保留对象。
|
||||
if let Some(key) = crate::expr::member::computed_const_key(&member.expression) {
|
||||
let idx = ctx.add_constant(Constant::String(key));
|
||||
let key_reg = ctx.alloc_reg();
|
||||
ctx.inst(Inst::load_const(Operand::Reg(key_reg), idx));
|
||||
let callee_reg = ctx.alloc_reg();
|
||||
ctx.inst(Inst::new(
|
||||
OpCode::LOAD_VAR,
|
||||
Operand::Reg(callee_reg),
|
||||
Operand::Reg(obj_reg),
|
||||
Operand::None,
|
||||
));
|
||||
ctx.inst(Inst::ic_get(Operand::Reg(callee_reg), Operand::Reg(key_reg)));
|
||||
(callee_reg, obj_reg)
|
||||
} else {
|
||||
let key_reg = self.emit_expression(&member.expression, ctx)?;
|
||||
let callee_reg = ctx.alloc_reg();
|
||||
ctx.inst(Inst::new(
|
||||
OpCode::GET_PROP_DYNAMIC,
|
||||
Operand::Reg(obj_reg),
|
||||
Operand::Reg(key_reg),
|
||||
Operand::Reg(callee_reg),
|
||||
));
|
||||
(callee_reg, obj_reg)
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
let callee_reg = self.emit_expression(&call.callee, ctx)?;
|
||||
|
||||
@@ -44,6 +44,15 @@ impl Emitter {
|
||||
fn emit_computed_member_expression(
|
||||
&self, member: &oxide_parser::ComputedMemberExpression, ctx: &mut CompileCtx,
|
||||
) -> Result<u32, String> {
|
||||
// 常量字符串键折叠为 IC 静态路径:免去运行期键 interning 与慢路径 ordinary_get。
|
||||
if let Some(key) = computed_const_key(&member.expression) {
|
||||
let obj_reg = self.emit_expression(&member.object, ctx)?;
|
||||
let idx = ctx.add_constant(Constant::String(key));
|
||||
let key_reg = ctx.alloc_reg();
|
||||
ctx.inst(Inst::load_const(Operand::Reg(key_reg), idx));
|
||||
ctx.inst(Inst::ic_get(Operand::Reg(obj_reg), Operand::Reg(key_reg)));
|
||||
return Ok(obj_reg);
|
||||
}
|
||||
let obj_reg = self.emit_expression(&member.object, ctx)?;
|
||||
let key_reg = self.emit_expression(&member.expression, ctx)?;
|
||||
let r = ctx.alloc_reg();
|
||||
@@ -105,3 +114,34 @@ impl Emitter {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// 判断字符串是否为规范数组下标键(无前导零的纯数字串,可解析为 u32)。
|
||||
///
|
||||
/// 数组元素区独立于 shape 槽:此类键经 `set_or_create_prop_value` 写入元素区,
|
||||
/// 不进 shape 链,IC 判定 `slot < prop_vec_len` 永不命中——折叠零收益且多付探试开销,
|
||||
/// 故排除。规则与 VM `array_index_from_property_key` 一致。
|
||||
pub(crate) fn is_array_index_str(s: &str) -> bool {
|
||||
if s.is_empty() || (s.len() > 1 && s.starts_with('0')) {
|
||||
return false;
|
||||
}
|
||||
s.parse::<u32>().is_ok()
|
||||
}
|
||||
|
||||
/// 提取计算成员表达式的常量字符串键(可折叠为 IC 静态路径)。
|
||||
///
|
||||
/// 只接受字符串字面量与无插值模板(单段、无表达式);数字字面量、含插值模板
|
||||
/// (运行期非常量 / 键求值有副作用)与规范数组下标串返回 `None`,维持 DYNAMIC 路径。
|
||||
pub(crate) fn computed_const_key(expr: &Expression) -> Option<String> {
|
||||
let key = match expr {
|
||||
Expression::StringLiteral(s) => s.value.to_string(),
|
||||
Expression::TemplateLiteral(tl) if tl.expressions.is_empty() && tl.quasis.len() == 1 => {
|
||||
tl.quasis[0].value.cooked.as_ref().map(|c| c.to_string()).unwrap_or_default()
|
||||
}
|
||||
_ => return None,
|
||||
};
|
||||
if is_array_index_str(&key) {
|
||||
None
|
||||
} else {
|
||||
Some(key)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -400,6 +400,35 @@ impl Emitter {
|
||||
}
|
||||
SimpleAssignmentTarget::ComputedMemberExpression(member) => {
|
||||
let obj_reg = self.emit_expression(&member.object, ctx)?;
|
||||
// 常量字符串键折叠为 IC 静态路径:MEMBER_INC/DEC 携带 IC 扩展字。
|
||||
if let Some(key) = crate::expr::member::computed_const_key(&member.expression) {
|
||||
let idx = ctx.add_constant(Constant::String(key));
|
||||
let key_reg = ctx.alloc_reg();
|
||||
ctx.inst(Inst::load_const(Operand::Reg(key_reg), idx));
|
||||
let val_reg = ctx.alloc_reg();
|
||||
let op = match update.operator {
|
||||
UpdateOperator::Increment => OpCode::MEMBER_INC,
|
||||
UpdateOperator::Decrement => OpCode::MEMBER_DEC,
|
||||
};
|
||||
match op {
|
||||
OpCode::MEMBER_INC => {
|
||||
ctx.inst(Inst::member_inc(
|
||||
Operand::Reg(obj_reg),
|
||||
Operand::Reg(val_reg),
|
||||
Operand::Reg(key_reg),
|
||||
));
|
||||
}
|
||||
OpCode::MEMBER_DEC => {
|
||||
ctx.inst(Inst::member_dec(
|
||||
Operand::Reg(obj_reg),
|
||||
Operand::Reg(val_reg),
|
||||
Operand::Reg(key_reg),
|
||||
));
|
||||
}
|
||||
_ => unreachable!(),
|
||||
}
|
||||
return Ok(val_reg);
|
||||
}
|
||||
let key_reg = self.emit_expression(&member.expression, ctx)?;
|
||||
let val_reg = ctx.alloc_reg();
|
||||
let op = match update.operator {
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
//! 函数:`emit_chainable_expression`、`emit_optional_guard`、`emit_chain_call` 等。
|
||||
|
||||
use crate::expr::call::pack_arg_regs;
|
||||
use crate::expr::member::is_array_index_str;
|
||||
use crate::{CompileCtx, Emitter};
|
||||
use oxide_bytecode::module::Constant;
|
||||
use oxide_bytecode::opcode::OpCode;
|
||||
@@ -44,6 +45,16 @@ impl Emitter {
|
||||
self.emit_optional_guard(obj_reg, label, ctx)?;
|
||||
}
|
||||
}
|
||||
// 常量字符串键折叠为 IC 静态路径:value 独立寄存器,base 保留供 this 绑定。
|
||||
if let Some(key) = crate::expr::member::computed_const_key(&member.expression) {
|
||||
let idx = ctx.add_constant(Constant::String(key));
|
||||
let key_reg = ctx.alloc_reg();
|
||||
ctx.inst(Inst::load_const(Operand::Reg(key_reg), idx));
|
||||
let value_reg = ctx.alloc_reg();
|
||||
ctx.inst(Inst::new(OpCode::LOAD_VAR, Operand::Reg(value_reg), Operand::Reg(obj_reg), Operand::None));
|
||||
ctx.inst(Inst::ic_get(Operand::Reg(value_reg), Operand::Reg(key_reg)));
|
||||
return Ok((value_reg, obj_reg));
|
||||
}
|
||||
let key_reg = self.emit_expression(&member.expression, ctx)?;
|
||||
let value_reg = ctx.alloc_reg();
|
||||
ctx.inst(Inst::new(
|
||||
@@ -289,6 +300,17 @@ impl Emitter {
|
||||
}
|
||||
let prop_reg = ctx.alloc_reg();
|
||||
ctx.inst(Inst::new(OpCode::LOAD_VAR, Operand::Reg(prop_reg), Operand::Reg(src_reg), Operand::None));
|
||||
// 字符串字面量计算键折叠为 IC 静态路径;标识符/数字键走 DYNAMIC。
|
||||
if let PropertyKey::StringLiteral(s) = key {
|
||||
let key_str = s.value.to_string();
|
||||
if !is_array_index_str(&key_str) {
|
||||
let key_idx = ctx.add_constant(Constant::String(key_str));
|
||||
let key_reg = ctx.alloc_reg();
|
||||
ctx.inst(Inst::load_const(Operand::Reg(key_reg), key_idx));
|
||||
ctx.inst(Inst::ic_get(Operand::Reg(prop_reg), Operand::Reg(key_reg)));
|
||||
return Ok((prop_reg, None, Some(key_reg)));
|
||||
}
|
||||
}
|
||||
let key_reg = self.emit_property_key_expression(key, ctx)?;
|
||||
let val_reg = ctx.alloc_reg();
|
||||
ctx.inst(Inst::new(
|
||||
|
||||
Reference in New Issue
Block a user