perf: 一元逻辑条件字面量表达式折叠为单常量

This commit is contained in:
2026-10-12 13:50:42 +00:00
parent 35efd73ea0
commit 01b47c030a
5 changed files with 297 additions and 29 deletions
Generated
+20 -20
View File
@@ -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",
+1 -1
View File
@@ -24,7 +24,7 @@ members = [
]
[workspace.package]
version = "0.16.16"
version = "0.16.18"
edition = "2021"
license = "MIT"
rust-version = "1.80"
+143 -7
View File
@@ -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<Constant> {
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<num_bigint::BigInt> {
if let Ok(v) = b.value.parse::<i128>() {
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<Co
Some(Constant::Boolean(if negate { !cmp } else { cmp }))
}
/// 一元折叠分派:按操作符计算折值,守卫命中返回 `None`。
///
/// 折值编码逐运算符镜像运行时产物:
/// - `LogicalNot`:`Boolean`(镜像 `NOT` 的 `to_boolean` 取反)
/// - `UnaryNegation`:BigInt 操作数折 `BigInt`,其余恒 `Number`(镜像
/// `NEG` 的 `JsValue::float`,不保 `Int`)
/// - `UnaryPlus`:BigInt 操作数不折(运行期抛 TypeError,保持运行时指令为
/// 该异常的唯一来源);其余恒 `Number`
/// - `BitwiseNot`:BigInt 操作数折 `BigInt`,其余恒 `Int`(镜像 `to_int32`)
/// - `Typeof`:`Int`/`Number` 折 "number"、`BigInt` 折 "bigint"、`Boolean` 折
/// "boolean"、`Null` 折 "object"(结果字符串经 `pool_key_plain` 入池,逐类型
/// 镜像运行时 `TYPEOF_TEXTS` 分派)
/// - `Void`:恒 `Undefined`
/// - `Delete`:引用语义非常量,不折(入口 Delete 特判先行,此臂为防御)
pub(crate) fn fold_unary(op: UnaryOperator, c: &Constant) -> Option<Constant> {
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<Constant> {
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<Constant> {
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` 后
@@ -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<u32, String> {
// 条件常量折叠: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<u32, String> {
// 逻辑常量折叠:两侧均为字面量时折单常量,发射单条 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) {
+101 -1
View File
@@ -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 {