From 01b47c030a42511dd5320e559e3b51fbb5a4fe0d Mon Sep 17 00:00:00 2001 From: pjh456 <147148383@qq.com> Date: Mon, 12 Oct 2026 13:50:42 +0000 Subject: [PATCH] =?UTF-8?q?perf:=20=E4=B8=80=E5=85=83=E9=80=BB=E8=BE=91?= =?UTF-8?q?=E6=9D=A1=E4=BB=B6=E5=AD=97=E9=9D=A2=E9=87=8F=E8=A1=A8=E8=BE=BE?= =?UTF-8?q?=E5=BC=8F=E6=8A=98=E5=8F=A0=E4=B8=BA=E5=8D=95=E5=B8=B8=E9=87=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Cargo.lock | 40 +++--- Cargo.toml | 2 +- crates/oxide_emit/src/expr/operator/fold.rs | 150 +++++++++++++++++++- crates/oxide_emit/src/expr/operator/mod.rs | 32 +++++ crates/oxide_emit/tests/fold_tests.rs | 102 ++++++++++++- 5 files changed, 297 insertions(+), 29 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 361a47c0..a4645e39 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1160,7 +1160,7 @@ dependencies = [ [[package]] name = "oxide_builtins" -version = "0.16.16" +version = "0.16.18" dependencies = [ "chrono", "fast-float", @@ -1185,7 +1185,7 @@ dependencies = [ [[package]] name = "oxide_bytecode" -version = "0.16.16" +version = "0.16.18" dependencies = [ "num-bigint", "oxide_log", @@ -1193,7 +1193,7 @@ dependencies = [ [[package]] name = "oxide_cfg" -version = "0.16.16" +version = "0.16.18" dependencies = [ "oxide_bytecode", "oxide_emit", @@ -1204,7 +1204,7 @@ dependencies = [ [[package]] name = "oxide_cli" -version = "0.16.16" +version = "0.16.18" dependencies = [ "ansi_term", "bumpalo", @@ -1228,7 +1228,7 @@ dependencies = [ [[package]] name = "oxide_code_cache" -version = "0.16.16" +version = "0.16.18" dependencies = [ "lru", "oxide_bytecode", @@ -1237,7 +1237,7 @@ dependencies = [ [[package]] name = "oxide_compiler" -version = "0.16.16" +version = "0.16.18" dependencies = [ "oxide_bytecode", "oxide_cfg", @@ -1257,7 +1257,7 @@ dependencies = [ [[package]] name = "oxide_dce" -version = "0.16.16" +version = "0.16.18" dependencies = [ "oxide_bytecode", "oxide_cfg", @@ -1271,7 +1271,7 @@ dependencies = [ [[package]] name = "oxide_emit" -version = "0.16.16" +version = "0.16.18" dependencies = [ "num-bigint", "oxc_allocator", @@ -1287,7 +1287,7 @@ dependencies = [ [[package]] name = "oxide_engine" -version = "0.16.16" +version = "0.16.18" dependencies = [ "oxide_bytecode", "oxide_compiler", @@ -1299,7 +1299,7 @@ dependencies = [ [[package]] name = "oxide_ir" -version = "0.16.16" +version = "0.16.18" dependencies = [ "oxide_bytecode", "oxide_log", @@ -1308,7 +1308,7 @@ dependencies = [ [[package]] name = "oxide_kernel" -version = "0.16.16" +version = "0.16.18" dependencies = [ "bumpalo", "dashmap", @@ -1322,7 +1322,7 @@ dependencies = [ [[package]] name = "oxide_liveness" -version = "0.16.16" +version = "0.16.18" dependencies = [ "oxide_bytecode", "oxide_cfg", @@ -1334,7 +1334,7 @@ dependencies = [ [[package]] name = "oxide_log" -version = "0.16.16" +version = "0.16.18" dependencies = [ "tracing", "tracing-appender", @@ -1343,11 +1343,11 @@ dependencies = [ [[package]] name = "oxide_net" -version = "0.16.16" +version = "0.16.18" [[package]] name = "oxide_parser" -version = "0.16.16" +version = "0.16.18" dependencies = [ "oxc_allocator", "oxc_ast", @@ -1362,7 +1362,7 @@ dependencies = [ [[package]] name = "oxide_regalloc" -version = "0.16.16" +version = "0.16.18" dependencies = [ "oxide_bytecode", "oxide_cfg", @@ -1378,7 +1378,7 @@ dependencies = [ [[package]] name = "oxide_runtime_api" -version = "0.16.16" +version = "0.16.18" dependencies = [ "num-bigint", "num-traits", @@ -1391,7 +1391,7 @@ dependencies = [ [[package]] name = "oxide_test262" -version = "0.16.16" +version = "0.16.18" dependencies = [ "oxide_bytecode", "oxide_compiler", @@ -1411,7 +1411,7 @@ dependencies = [ [[package]] name = "oxide_types" -version = "0.16.16" +version = "0.16.18" dependencies = [ "bumpalo", "dashmap", @@ -1424,7 +1424,7 @@ dependencies = [ [[package]] name = "oxide_vm" -version = "0.16.16" +version = "0.16.18" dependencies = [ "bumpalo", "chrono", diff --git a/Cargo.toml b/Cargo.toml index 69b62627..54149c2b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -24,7 +24,7 @@ members = [ ] [workspace.package] -version = "0.16.16" +version = "0.16.18" edition = "2021" license = "MIT" rust-version = "1.80" diff --git a/crates/oxide_emit/src/expr/operator/fold.rs b/crates/oxide_emit/src/expr/operator/fold.rs index cd0aead9..20583eba 100644 --- a/crates/oxide_emit/src/expr/operator/fold.rs +++ b/crates/oxide_emit/src/expr/operator/fold.rs @@ -2,28 +2,31 @@ //! 发射单条 `LOAD_CONST`。折值编码逐运算符镜像运行时指令的产物编码, //! 保证物化后与运行时执行结果逐字节一致。 //! -//! 两个域并列:数值域(`literal_constant`/`fold_binary`,两操作数形态, -//! 守卫排除字符串与 BigInt)与字符串域(`literal_units`/`fold_string_chain`/ +//! 三个域并列:数值域(`literal_constant`/`fold_binary`,两操作数形态, +//! 守卫排除字符串与 BigInt)、字符串域(`literal_units`/`fold_string_chain`/ //! `fold_template`,Addition 链与全字面量模板,单元口径与运行期 -//! `push_units_to` 逐段一致)。 +//! `push_units_to` 逐段一致)、一元/逻辑/条件域(`literal_constant_wide`/ +//! `fold_unary`/`fold_logical`/`fold_conditional`,全字面量操作数折单常量)。 //! //! 数值域语义守卫:任一操作数为 BigInt 字面量不折(BigInt 混合运行期抛 //! TypeError,BigInt 与 BigInt 有独立指令族);`DIV`/`MOD` 除数为零不折、 //! `EXP` 指数为负不折(保持运行时指令为除零/负指数编码的唯一来源)。 //! 字符串域语义守卫:Addition 链须至少一个字符串字面量(全数字链落回 //! 数值域与 `CONCAT_N`,两域零重叠);模板须全部 cooked 为 Some。 +//! 一元/逻辑/条件域语义守卫:`+` 作用 BigInt 字面量不折(运行期抛 +//! TypeError,保持运行时指令为该异常的唯一来源)。 use oxide_bytecode::module::Constant; -use oxide_parser::{BinaryOperator, Expression, UnaryOperator}; +use oxide_parser::{BinaryOperator, Expression, LogicalOperator, UnaryOperator}; use oxide_runtime_api::{ - abstract_eq_primitives, bigint_to_string, js_number_to_string, relational_compare, strict_equality, to_int32, - to_number, to_uint32, + abstract_eq_primitives, bigint_to_string, js_number_to_string, relational_compare, strict_equality, to_boolean, + to_int32, to_number, to_uint32, }; use oxide_types::string_forge::{decode_key, encode_key}; use oxide_types::value::JsValue; use crate::is_int_literal; -use crate::shared::string_pool::pool_key_marker; +use crate::shared::string_pool::{pool_key_marker, pool_key_plain}; /// 字面量表达式提取常量:剥括号与一元负号后,数字(`is_int_literal` 分派 /// `Int`/`Number`)、布尔、null 三类;BigInt 字面量、字符串字面量、RegExp 与 @@ -85,6 +88,57 @@ fn const_to_number(c: &Constant) -> f64 { to_number(const_to_jsvalue(c)) } +/// 字面量表达式提取常量(宽口径):窄口径三臂加 BigInt 字面量臂,供一元/逻辑/ +/// 条件折叠识别 BigInt 字面量(`+` 的 TypeError 守卫与 `-`/`~` 的 BigInt 折值 +/// 都须知道操作数是 BigInt)。 +/// +/// 负零字面量(奇数层负号作用于 0 字面量)保 `Number(-0.0)` 不经 Int 臂,防 +/// 符号丢失(`+`/`-` 折叠产物经 `Object.is` 可观察)。BigInt 字面量解析与 +/// 字面量发射器同口径,解析失败返回 `None` 不折。 +pub(crate) fn literal_constant_wide(expr: &Expression) -> Option { + let (inner, negated) = peel_negation(expr); + match inner { + Expression::NumericLiteral(n) => { + // 负零守卫:奇数层负号作用于 0 字面量保 Number(-0.0),Int 臂会丢符号。 + if negated && n.value == 0.0 { + return Some(Constant::Number(-0.0)); + } + let value = if negated { -n.value } else { n.value }; + if is_int_literal(value) { + Some(Constant::Int(value as i32)) + } else { + Some(Constant::Number(value)) + } + } + Expression::BooleanLiteral(b) if !negated => Some(Constant::Boolean(b.value)), + Expression::NullLiteral(_) if !negated => Some(Constant::Null), + Expression::BigIntLiteral(b) => { + let value = parse_bigint_literal(b)?; + Some(if negated { Constant::BigInt(-value) } else { Constant::BigInt(value) }) + } + _ => None, + } +} + +/// BigInt 字面量解析:与字面量发射器同口径(i128 快路径失败回退十进制 +/// `parse_bytes`,解析失败返回 `None` 不折)。 +fn parse_bigint_literal(b: &oxide_parser::BigIntLiteral) -> Option { + if let Ok(v) = b.value.parse::() { + Some(num_bigint::BigInt::from(v)) + } else { + num_bigint::BigInt::parse_bytes(b.value.as_bytes(), 10) + } +} + +/// 常量的布尔值(`ToBoolean` 口径):BigInt 臂直接判零,其余经 +/// `const_to_jsvalue` 加 `to_boolean`。 +fn const_to_boolean(c: &Constant) -> bool { + if let Constant::BigInt(b) = c { + return *b != num_bigint::BigInt::from(0); + } + to_boolean(const_to_jsvalue(c)) +} + /// 双常量折叠分派:按操作码计算折值,守卫命中返回 `None`。 /// /// 折值编码逐运算符镜像运行时产物: @@ -198,6 +252,88 @@ fn relational(l: &Constant, r: &Constant, swap: bool, negate: bool) -> Option Option { + match op { + UnaryOperator::LogicalNot => Some(Constant::Boolean(!const_to_boolean(c))), + UnaryOperator::UnaryNegation => match c { + Constant::BigInt(b) => Some(Constant::BigInt(-b.clone())), + _ => Some(Constant::Number(-const_to_number(c))), + }, + UnaryOperator::UnaryPlus => { + // BigInt 守卫:运行期抛 TypeError,不折。 + if matches!(c, Constant::BigInt(_)) { + return None; + } + Some(Constant::Number(const_to_number(c))) + } + UnaryOperator::BitwiseNot => match c { + Constant::BigInt(b) => Some(Constant::BigInt(!b.clone())), + _ => Some(Constant::Int(!to_int32(const_to_jsvalue(c)))), + }, + UnaryOperator::Typeof => { + // 逐类型镜像运行时 `TYPEOF_TEXTS` 分派:BigInt 归 "bigint" + // (独立于 Int/Double 的 "number"),Boolean 归 "boolean",Null 归 "object"。 + let text = match c { + Constant::Int(_) | Constant::Number(_) => "number", + Constant::BigInt(_) => "bigint", + Constant::Boolean(_) => "boolean", + Constant::Null => "object", + _ => return None, + }; + Some(Constant::String(pool_key_plain(text))) + } + UnaryOperator::Void => Some(Constant::Undefined), + UnaryOperator::Delete => None, + } +} + +/// 逻辑折叠分派:结果恒为某一操作数的原样拷贝(无类型转换,任何字面量类型 +/// 均安全,含 BigInt)。 +pub(crate) fn fold_logical(op: LogicalOperator, l: &Constant, r: &Constant) -> Option { + Some(match op { + LogicalOperator::And => { + if const_to_boolean(l) { + r.clone() + } else { + l.clone() + } + } + LogicalOperator::Or => { + if const_to_boolean(l) { + l.clone() + } else { + r.clone() + } + } + LogicalOperator::Coalesce => { + if matches!(l, Constant::Null | Constant::Undefined) { + r.clone() + } else { + l.clone() + } + } + }) +} + +/// 条件折叠分派:结果恒为 test 的布尔值选定的分支常量(原样拷贝)。 +pub(crate) fn fold_conditional(test: &Constant, cons: &Constant, alt: &Constant) -> Option { + Some(if const_to_boolean(test) { cons.clone() } else { alt.clone() }) +} + /// 可折叠字面量的单元序列(剥括号与一元负号链):字符串字面量经 /// `pool_key_marker` 加 `decode_key` 还原单元(与字面量发射器同一池键函数); /// 数字字面量按 `is_int_literal` 分派 i32 itoa 或 `js_number_to_string` 后 diff --git a/crates/oxide_emit/src/expr/operator/mod.rs b/crates/oxide_emit/src/expr/operator/mod.rs index 7a304f30..4aba1348 100644 --- a/crates/oxide_emit/src/expr/operator/mod.rs +++ b/crates/oxide_emit/src/expr/operator/mod.rs @@ -109,6 +109,16 @@ impl Emitter { } return self.emit_delete_operand(operand, ctx); } + // 一元常量折叠:操作数为字面量且通过语义守卫时折单常量,发射单条 + // LOAD_CONST(编码镜像运行时指令产物,见 fold.rs)。 + if let Some(c) = fold::literal_constant_wide(&un.argument) { + if let Some(folded) = fold::fold_unary(un.operator, &c) { + let idx = ctx.add_constant(folded); + let r = ctx.alloc_reg(); + ctx.inst(Inst::load_const(Operand::Reg(r), idx)); + return Ok(r); + } + } let arg = if matches!(un.operator, UnaryOperator::Typeof) { self.emit_typeof_operand(&un.argument, ctx)? } else { @@ -446,6 +456,19 @@ impl Emitter { fn emit_conditional_expression( &self, cond: &oxide_parser::ConditionalExpression, ctx: &mut CompileCtx, ) -> Result { + // 条件常量折叠:test 与两臂均为字面量时折单常量,发射单条 LOAD_CONST。 + if let (Some(t), Some(c), Some(a)) = ( + fold::literal_constant_wide(&cond.test), + fold::literal_constant_wide(&cond.consequent), + fold::literal_constant_wide(&cond.alternate), + ) { + if let Some(folded) = fold::fold_conditional(&t, &c, &a) { + let idx = ctx.add_constant(folded); + let r = ctx.alloc_reg(); + ctx.inst(Inst::load_const(Operand::Reg(r), idx)); + return Ok(r); + } + } let else_label = ctx.next_label_id(); let end_label = ctx.next_label_id(); @@ -479,6 +502,15 @@ impl Emitter { fn emit_logical_expression( &self, log: &oxide_parser::LogicalExpression, ctx: &mut CompileCtx, ) -> Result { + // 逻辑常量折叠:两侧均为字面量时折单常量,发射单条 LOAD_CONST。 + if let (Some(l), Some(r)) = (fold::literal_constant_wide(&log.left), fold::literal_constant_wide(&log.right)) { + if let Some(folded) = fold::fold_logical(log.operator, &l, &r) { + let idx = ctx.add_constant(folded); + let r = ctx.alloc_reg(); + ctx.inst(Inst::load_const(Operand::Reg(r), idx)); + return Ok(r); + } + } let left_reg = self.emit_expression(&log.left, ctx)?; if is_side_effect_free(&log.left) && is_side_effect_free(&log.right) { diff --git a/crates/oxide_emit/tests/fold_tests.rs b/crates/oxide_emit/tests/fold_tests.rs index 62dabf43..79d8afec 100644 --- a/crates/oxide_emit/tests/fold_tests.rs +++ b/crates/oxide_emit/tests/fold_tests.rs @@ -102,6 +102,101 @@ fn fold_guard_matrix() { } } +/// 一元/逻辑/条件命中矩阵:每行一条源串,断言目标操作码计数为 0 且常量池含折值。 +/// 折值编码逐运算符镜像运行时产物(`NEG`/`UNARY_PLUS` 恒 `Number`、`BIT_NOT` 恒 +/// `Int`、`-`/`~` 对 BigInt 折 `BigInt`、`NOT` 恒 `Boolean`、`TYPEOF` 恒 `String`、 +/// `VOID` 恒 `Undefined`、逻辑与条件恒操作数原样拷贝)。 +#[test] +fn fold_unary_logical_conditional_hit_matrix() { + // (源串, 目标操作码, 期望折值) + let cases: &[(&str, OpCode, Constant)] = &[ + // 一元:NOT。 + ("!0", OpCode::NOT, Constant::Boolean(true)), + ("!1", OpCode::NOT, Constant::Boolean(false)), + ("!0n", OpCode::NOT, Constant::Boolean(true)), + ("!1n", OpCode::NOT, Constant::Boolean(false)), + // 一元:NEG 恒 Number(不保 Int),BigInt 折 BigInt。 + ("-1", OpCode::NEG, Constant::Number(-1.0)), + ("-1.5", OpCode::NEG, Constant::Number(-1.5)), + // 双层负号经剥壳。 + ("-(-1)", OpCode::NEG, Constant::Number(1.0)), + ("-10n", OpCode::NEG, Constant::BigInt(num_bigint::BigInt::from(-10))), + // 一元:UNARY_PLUS 恒 Number。 + ("+1", OpCode::UNARY_PLUS, Constant::Number(1.0)), + ("+true", OpCode::UNARY_PLUS, Constant::Number(1.0)), + ("+null", OpCode::UNARY_PLUS, Constant::Number(0.0)), + // 负零守卫:奇数层负号作用于 0 字面量保 Number(-0.0)。 + ("+(-0.0)", OpCode::UNARY_PLUS, Constant::Number(-0.0)), + // 一元:BIT_NOT 恒 Int,BigInt 折 BigInt。 + ("~1", OpCode::BIT_NOT, Constant::Int(-2)), + ("~true", OpCode::BIT_NOT, Constant::Int(-2)), + ("~10n", OpCode::BIT_NOT, Constant::BigInt(num_bigint::BigInt::from(-11))), + // 一元:TYPEOF 恒 String(BigInt 归 "bigint",独立于 "number")。 + ("typeof 1", OpCode::TYPEOF, Constant::String("number".into())), + ("typeof true", OpCode::TYPEOF, Constant::String("boolean".into())), + ("typeof null", OpCode::TYPEOF, Constant::String("object".into())), + ("typeof 1n", OpCode::TYPEOF, Constant::String("bigint".into())), + // 一元:VOID 恒 Undefined。 + ("void 1", OpCode::VOID, Constant::Undefined), + ("void 0n", OpCode::VOID, Constant::Undefined), + // 逻辑:恒操作数原样拷贝。 + ("1 && 2", OpCode::AND, Constant::Int(2)), + ("0 && 2", OpCode::AND, Constant::Int(0)), + ("true || 2", OpCode::OR, Constant::Boolean(true)), + ("0 || 2", OpCode::OR, Constant::Int(2)), + ("1n && 2n", OpCode::AND, Constant::BigInt(num_bigint::BigInt::from(2))), + ("0n && 2n", OpCode::AND, Constant::BigInt(num_bigint::BigInt::from(0))), + ("null ?? 1", OpCode::NULLISH, Constant::Int(1)), + ("1 ?? 2", OpCode::NULLISH, Constant::Int(1)), + ("1n ?? 2n", OpCode::NULLISH, Constant::BigInt(num_bigint::BigInt::from(1))), + // 条件:test 布尔值选定分支。 + ("1 ? 2 : 3", OpCode::JMP_IF_FALSE, Constant::Int(2)), + ("0 ? 2 : 3", OpCode::JMP_IF_FALSE, Constant::Int(3)), + ("true ? 1n : 2n", OpCode::JMP_IF_FALSE, Constant::BigInt(num_bigint::BigInt::from(1))), + ]; + for (src, op, folded) in cases { + let ir = emit_ir(src); + assert_eq!(count_op(&ir.insts, *op), 0, "{src:?} 应折叠,目标操作码 {op:?} 计数应为 0"); + assert!(pool_contains(&ir, folded), "{src:?} 常量池应含折值 {folded:?}"); + } +} + +/// 一元/逻辑/条件守卫矩阵:非字面量操作数落回运行时路径,每条带显式期望计数 +/// (落回路径操作码因运算符而异,不复用二元守卫的「计数为 1」单操作码断言)。 +#[test] +fn fold_unary_logical_conditional_guard_matrix() { + // (源串, 目标操作码, 期望计数) + let cases: &[(&str, OpCode, usize)] = &[ + // + 作用 BigInt:运行期 TypeError 唯一来源,不折。 + ("+1n", OpCode::UNARY_PLUS, 1), + // 剥壳后仍 BigInt。 + ("+(-1n)", OpCode::UNARY_PLUS, 1), + // typeof 操作数为二元表达式(非字面量),不折。 + ("typeof (1 + 2)", OpCode::TYPEOF, 1), + // typeof 未声明标识符走 LOAD_GLOBAL_TYPEOF 特判加 TYPEOF,不折。 + ("typeof x", OpCode::TYPEOF, 1), + // 逻辑短路路径(非字面量操作数)。 + ("x && 1", OpCode::AND, 0), + ("1 && x", OpCode::AND, 0), + ("x || 1", OpCode::OR, 0), + ("x ?? 1", OpCode::NULLISH, 0), + // 条件:consequent / test 非字面量。 + ("1 ? x : 2", OpCode::JMP_IF_FALSE, 1), + ("x ? 1 : 2", OpCode::JMP_IF_FALSE, 1), + ]; + for (src, op, expected) in cases { + let ir = emit_ir(src); + assert_eq!( + count_op(&ir.insts, *op), + *expected, + "{src:?} 守卫命中,目标操作码 {op:?} 计数应为 {expected}" + ); + } + // typeof 未声明标识符走 LOAD_GLOBAL_TYPEOF 特判路径。 + let ir = emit_ir("typeof x"); + assert_eq!(count_op(&ir.insts, OpCode::LOAD_GLOBAL_TYPEOF), 1, "typeof x 应发 LOAD_GLOBAL_TYPEOF"); +} + /// 字节码指纹:`CompiledModule` 未派生 `Debug`,逐字段展开为确定串(子模块递归)。 fn bytecode_fingerprint(m: &oxide_bytecode::module::CompiledModule) -> String { let mut out = format!( @@ -124,7 +219,12 @@ fn bytecode_fingerprint(m: &oxide_bytecode::module::CompiledModule) -> String { /// 确定性:同一源串连续编译八次,IR 与字节码指纹全等(折叠路径纯函数无迭代序依赖)。 #[test] fn fold_deterministic() { - let srcs = ["1 + 2 * 3;", "var a = -1 >>> 0; var b = 1 < 2;", "var c = 5 & 3 | 4;"]; + let srcs = [ + "1 + 2 * 3;", + "var a = -1 >>> 0; var b = 1 < 2;", + "var c = 5 & 3 | 4;", + "var a = !1 && 2; var b = 1 ? 2 : 3; var c = typeof 1n; var d = 0n ?? 1n;", + ]; for src in srcs { let mut fingerprints: Vec<(String, String)> = Vec::new(); for _ in 0..8 {