mirror of
https://github.com/pjh456/OxideJS.git
synced 2026-10-23 18:21:13 +08:00
perf: 常量折叠加位运算与比较 C 组加池溢出守卫
This commit is contained in:
Generated
+21
-21
@@ -1160,7 +1160,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "oxide_builtins"
|
||||
version = "0.16.21"
|
||||
version = "0.16.22"
|
||||
dependencies = [
|
||||
"chrono",
|
||||
"fast-float",
|
||||
@@ -1185,7 +1185,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "oxide_bytecode"
|
||||
version = "0.16.21"
|
||||
version = "0.16.22"
|
||||
dependencies = [
|
||||
"num-bigint",
|
||||
"oxide_log",
|
||||
@@ -1193,7 +1193,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "oxide_cfg"
|
||||
version = "0.16.21"
|
||||
version = "0.16.22"
|
||||
dependencies = [
|
||||
"oxide_bytecode",
|
||||
"oxide_emit",
|
||||
@@ -1204,7 +1204,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "oxide_cli"
|
||||
version = "0.16.21"
|
||||
version = "0.16.22"
|
||||
dependencies = [
|
||||
"ansi_term",
|
||||
"bumpalo",
|
||||
@@ -1228,7 +1228,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "oxide_code_cache"
|
||||
version = "0.16.21"
|
||||
version = "0.16.22"
|
||||
dependencies = [
|
||||
"lru",
|
||||
"oxide_bytecode",
|
||||
@@ -1237,7 +1237,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "oxide_compiler"
|
||||
version = "0.16.21"
|
||||
version = "0.16.22"
|
||||
dependencies = [
|
||||
"oxide_bytecode",
|
||||
"oxide_cfg",
|
||||
@@ -1258,7 +1258,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "oxide_dce"
|
||||
version = "0.16.21"
|
||||
version = "0.16.22"
|
||||
dependencies = [
|
||||
"oxide_bytecode",
|
||||
"oxide_cfg",
|
||||
@@ -1272,7 +1272,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "oxide_emit"
|
||||
version = "0.16.21"
|
||||
version = "0.16.22"
|
||||
dependencies = [
|
||||
"num-bigint",
|
||||
"oxc_allocator",
|
||||
@@ -1288,7 +1288,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "oxide_engine"
|
||||
version = "0.16.21"
|
||||
version = "0.16.22"
|
||||
dependencies = [
|
||||
"oxide_bytecode",
|
||||
"oxide_compiler",
|
||||
@@ -1300,7 +1300,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "oxide_fold"
|
||||
version = "0.16.21"
|
||||
version = "0.16.22"
|
||||
dependencies = [
|
||||
"num-bigint",
|
||||
"oxide_bytecode",
|
||||
@@ -1312,7 +1312,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "oxide_ir"
|
||||
version = "0.16.21"
|
||||
version = "0.16.22"
|
||||
dependencies = [
|
||||
"oxide_bytecode",
|
||||
"oxide_log",
|
||||
@@ -1321,7 +1321,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "oxide_kernel"
|
||||
version = "0.16.21"
|
||||
version = "0.16.22"
|
||||
dependencies = [
|
||||
"bumpalo",
|
||||
"dashmap",
|
||||
@@ -1335,7 +1335,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "oxide_liveness"
|
||||
version = "0.16.21"
|
||||
version = "0.16.22"
|
||||
dependencies = [
|
||||
"oxide_bytecode",
|
||||
"oxide_cfg",
|
||||
@@ -1347,7 +1347,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "oxide_log"
|
||||
version = "0.16.21"
|
||||
version = "0.16.22"
|
||||
dependencies = [
|
||||
"tracing",
|
||||
"tracing-appender",
|
||||
@@ -1356,11 +1356,11 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "oxide_net"
|
||||
version = "0.16.21"
|
||||
version = "0.16.22"
|
||||
|
||||
[[package]]
|
||||
name = "oxide_parser"
|
||||
version = "0.16.21"
|
||||
version = "0.16.22"
|
||||
dependencies = [
|
||||
"oxc_allocator",
|
||||
"oxc_ast",
|
||||
@@ -1375,7 +1375,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "oxide_regalloc"
|
||||
version = "0.16.21"
|
||||
version = "0.16.22"
|
||||
dependencies = [
|
||||
"oxide_bytecode",
|
||||
"oxide_cfg",
|
||||
@@ -1391,7 +1391,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "oxide_runtime_api"
|
||||
version = "0.16.21"
|
||||
version = "0.16.22"
|
||||
dependencies = [
|
||||
"num-bigint",
|
||||
"num-traits",
|
||||
@@ -1404,7 +1404,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "oxide_test262"
|
||||
version = "0.16.21"
|
||||
version = "0.16.22"
|
||||
dependencies = [
|
||||
"oxide_bytecode",
|
||||
"oxide_compiler",
|
||||
@@ -1424,7 +1424,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "oxide_types"
|
||||
version = "0.16.21"
|
||||
version = "0.16.22"
|
||||
dependencies = [
|
||||
"bumpalo",
|
||||
"dashmap",
|
||||
@@ -1437,7 +1437,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "oxide_vm"
|
||||
version = "0.16.21"
|
||||
version = "0.16.22"
|
||||
dependencies = [
|
||||
"bumpalo",
|
||||
"chrono",
|
||||
|
||||
+1
-1
@@ -25,7 +25,7 @@ members = [
|
||||
]
|
||||
|
||||
[workspace.package]
|
||||
version = "0.16.21"
|
||||
version = "0.16.22"
|
||||
edition = "2021"
|
||||
license = "MIT"
|
||||
rust-version = "1.80"
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
//! 折叠表:一元与算术常量折叠(A 组)加逻辑短路选支(B 组)。
|
||||
//! 折叠表:一元与算术常量折叠(A 组)、位运算与比较折叠(C 组)加逻辑短路
|
||||
//! 选支(B 组)。
|
||||
//!
|
||||
//! 折值编码逐运算符镜像运行时产物编码,保证物化后与运行时执行结果逐字节
|
||||
//! 一致:折值计算复用 `oxide_runtime_api::coercion` 的纯函数族(`to_number` /
|
||||
//! `to_int32` / `to_boolean`),与 emit 层字面量折叠同源,杜绝两层折叠各自
|
||||
//! 镜像运行时语义的漂移风险。
|
||||
//! `to_int32` / `to_uint32` / `to_boolean` / `relational_compare` /
|
||||
//! `abstract_eq_primitives` / `strict_equality`),与 emit 层字面量折叠同源,
|
||||
//! 杜绝两层折叠各自镜像运行时语义的漂移风险。
|
||||
//!
|
||||
//! 语义守卫:
|
||||
//! - 算术二元(ADD/SUB/MUL/DIV/MOD/EXP)任一操作数非数值不折;BigInt 混合
|
||||
@@ -15,13 +17,19 @@
|
||||
//! (镜像运行时 `int_add`);其余组合 f64 加(不做恒等捷径,负零操作数
|
||||
//! 经实加得 `+0.0`)。
|
||||
//! - UNARY_PLUS 作用 BigInt 不折(运行期抛 TypeError)。
|
||||
//! - C 组位运算与比较:双操作数须均在 `Int`/`Number`/`Boolean`/`Null` 四变体
|
||||
//! 域,`BigInt`/`String`/`Undefined` 操作数不折(与共享表 `const_to_jsvalue`
|
||||
//! 四臂口径一致,字符串比较路径降为后续任务)。
|
||||
//!
|
||||
//! B 组逻辑短路:结果恒为某一操作数的原值(无类型转换,任何类型安全),
|
||||
//! 仅左操作数已知即可选支,右操作数类型不限。
|
||||
|
||||
use oxide_bytecode::module::Constant;
|
||||
use oxide_bytecode::opcode::OpCode;
|
||||
use oxide_runtime_api::{to_boolean, to_int32, to_number};
|
||||
use oxide_runtime_api::{
|
||||
abstract_eq_primitives, relational_compare, strict_equality, to_boolean, to_int32, to_number,
|
||||
to_uint32,
|
||||
};
|
||||
use oxide_types::string_forge::encode_key;
|
||||
use oxide_types::value::JsValue;
|
||||
|
||||
@@ -196,3 +204,82 @@ pub fn short_circuit(op: OpCode, l: &Constant) -> Option<bool> {
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
/// C 组操作数域守卫:仅 `Int`/`Number`/`Boolean`/`Null` 四变体可折。
|
||||
///
|
||||
/// 传播映射可承载 `BigInt`/`String`/`Undefined` 常量(来自 `LOAD_CONST`),
|
||||
/// 这三类操作数不折(与共享表 `const_to_jsvalue` 四臂口径一致)。
|
||||
fn is_foldable_c_operand(c: &Constant) -> bool {
|
||||
matches!(c, Constant::Int(_) | Constant::Number(_) | Constant::Boolean(_) | Constant::Null)
|
||||
}
|
||||
|
||||
/// C 组位运算与比较折叠:按操作码计算折值,守卫命中返回 `None`。
|
||||
///
|
||||
/// 折值编码逐运算符镜像运行时产物:
|
||||
/// - `BIT_AND`/`BIT_OR`/`BIT_XOR`:`Int`(`ToInt32` 按位与/或/异或)
|
||||
/// - `SHL`/`SHR`:`Int`(`ToInt32` 左移/右移,移位量 `ToUint32` 与 0x1F)
|
||||
/// - `USHR`:结果不超 i32::MAX 保 `Int`,否则升 `Number`(镜像
|
||||
/// `write_ushr_result`)
|
||||
/// - `LT`/`GT`/`LTE`/`GTE`:`Boolean`(镜像 `relational_compare`,GT/LTE
|
||||
/// 交换操作数,LTE/GTE 取反,`None` 归 false)
|
||||
/// - `EQ`/`NEQ`:`Boolean`(镜像 `abstract_eq_primitives`)
|
||||
/// - `STRICT_EQ`/`STRICT_NEQ`:`Boolean`(镜像 `strict_equality`)
|
||||
///
|
||||
/// 操作数域守卫:双操作数须均在 `Int`/`Number`/`Boolean`/`Null` 四变体域,
|
||||
/// `BigInt`/`String`/`Undefined` 操作数不折(命中即返回 `None`)。
|
||||
pub fn fold_c_group(op: OpCode, l: &Constant, r: &Constant) -> Option<Constant> {
|
||||
// 操作数域守卫先行:传播映射可承载 BigInt/String/Undefined 常量,
|
||||
// C 组表只接受四变体域。
|
||||
if !is_foldable_c_operand(l) || !is_foldable_c_operand(r) {
|
||||
return None;
|
||||
}
|
||||
let (lv, rv) = (const_to_jsvalue(l)?, const_to_jsvalue(r)?);
|
||||
match op {
|
||||
OpCode::BIT_AND | OpCode::BIT_OR | OpCode::BIT_XOR => {
|
||||
let (a, b) = (to_int32(lv), to_int32(rv));
|
||||
Some(match op {
|
||||
OpCode::BIT_AND => Constant::Int(a & b),
|
||||
OpCode::BIT_OR => Constant::Int(a | b),
|
||||
_ => Constant::Int(a ^ b),
|
||||
})
|
||||
}
|
||||
OpCode::SHL | OpCode::SHR => {
|
||||
let a = to_int32(lv);
|
||||
let shift = to_uint32(rv) & 0x1F;
|
||||
Some(match op {
|
||||
OpCode::SHL => Constant::Int(a << shift),
|
||||
_ => Constant::Int(a >> shift),
|
||||
})
|
||||
}
|
||||
OpCode::USHR => {
|
||||
let a = to_int32(lv) as u32;
|
||||
let shift = to_uint32(rv) & 0x1F;
|
||||
let result = a >> shift;
|
||||
// 结果不超 i32::MAX 保 Int,否则升 Number(镜像 write_ushr_result)。
|
||||
if result <= i32::MAX as u32 {
|
||||
Some(Constant::Int(result as i32))
|
||||
} else {
|
||||
Some(Constant::Number(result as f64))
|
||||
}
|
||||
}
|
||||
OpCode::LT | OpCode::GT | OpCode::LTE | OpCode::GTE => {
|
||||
// GT/LTE 交换操作数,LTE/GTE 取反(镜像运行时派发口径)。
|
||||
let (a, b) = match op {
|
||||
OpCode::GT | OpCode::LTE => (rv, lv),
|
||||
_ => (lv, rv),
|
||||
};
|
||||
let cmp = relational_compare(a, b).unwrap_or(false);
|
||||
let negate = matches!(op, OpCode::LTE | OpCode::GTE);
|
||||
Some(Constant::Boolean(if negate { !cmp } else { cmp }))
|
||||
}
|
||||
OpCode::EQ | OpCode::NEQ => {
|
||||
let eq = abstract_eq_primitives(lv, rv);
|
||||
Some(Constant::Boolean(if op == OpCode::NEQ { !eq } else { eq }))
|
||||
}
|
||||
OpCode::STRICT_EQ | OpCode::STRICT_NEQ => {
|
||||
let eq = strict_equality(lv, rv);
|
||||
Some(Constant::Boolean(if op == OpCode::STRICT_NEQ { !eq } else { eq }))
|
||||
}
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,7 +3,8 @@
|
||||
//! `fold` 对每个函数(递归 nested,每个 nested 函数有独立常量池与指令流,独立前扫)
|
||||
//! 运行单向前扫:维护 per-register known 表记录各虚拟寄存器的已知常量值,命中
|
||||
//! 改写表即就地改写——A 组一元与算术常量折叠改写为 `LOAD_CONST`(新常量入池),
|
||||
//! B 组逻辑短路改写为 `MOV`(无新常量)。
|
||||
//! C 组位运算与比较常量折叠改写为 `LOAD_CONST`(新常量入池),B 组逻辑短路
|
||||
//! 改写为 `MOV`(无新常量)。
|
||||
//!
|
||||
//! 不变量:
|
||||
//! - 不删指令、不重映射 `label_pos`:改写只改变指令的 op 与操作数,不改变跳转
|
||||
@@ -12,12 +13,13 @@
|
||||
//! 跨汇合的陈旧知识不用于折叠。
|
||||
//! - 负零守卫:算术折叠做实际运算(f64 加),不取恒等捷径——`-0.0 + 0.0` 得
|
||||
//! `+0.0`(`Object.is` 可区分)。
|
||||
//! - 池溢出守卫:常量池满(`u16::MAX`)时 A 组改写禁用(不新增常量),B 组
|
||||
//! (MOV)不受影响。
|
||||
//! - 池溢出守卫:常量池满(`u16::MAX`)时 A 组与 C 组改写禁用(不新增常量),
|
||||
//! B 组(MOV)不受影响;`const_overflow` 已被 emit 层置位时整个 pass 跳过。
|
||||
//!
|
||||
//! 折值计算复用 `oxide_runtime_api::coercion` 的纯函数族(`to_number` /
|
||||
//! `to_int32` / `to_boolean`),与 emit 层字面量折叠同源,杜绝两层折叠各自镜像
|
||||
//! 运行时语义的漂移风险。
|
||||
//! `to_int32` / `to_uint32` / `to_boolean` / `relational_compare` /
|
||||
//! `abstract_eq_primitives` / `strict_equality`),与 emit 层字面量折叠同源,
|
||||
//! 杜绝两层折叠各自镜像运行时语义的漂移风险。
|
||||
|
||||
mod fold_log;
|
||||
mod fold_table;
|
||||
|
||||
@@ -23,7 +23,15 @@ use crate::fold_table;
|
||||
/// 返回是否发生改写(仅调试日志用)。不删指令、不重映射 label;改写只改变指令
|
||||
/// 的 op 与操作数,不改变跳转结构,CFG 的块划分与边集不变,由下游 DCE 在折叠后
|
||||
/// 的指令流上重建 CFG 吸收。
|
||||
///
|
||||
/// pass 级前置:`const_overflow` 已被 emit 层置位时程序已知无效,直接返回不改写
|
||||
/// (无折叠无效程序的意义,且加常量会雪上加霜)。
|
||||
pub fn fold_function(f: &mut IRFunction) -> bool {
|
||||
// pass 级前置:const_overflow 已置位即程序已知无效,跳过整个 pass。
|
||||
if f.const_overflow {
|
||||
return false;
|
||||
}
|
||||
|
||||
let insts = &mut f.insts;
|
||||
let constants = &mut f.constants;
|
||||
let label_pos = &f.label_pos;
|
||||
@@ -106,6 +114,20 @@ pub fn fold_function(f: &mut IRFunction) -> bool {
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
// C 组位运算与比较:两操作数须为已知寄存器且均在四变体域
|
||||
// (BigInt/String/Undefined 操作数由表内守卫不折)。
|
||||
OpCode::BIT_AND | OpCode::BIT_OR | OpCode::BIT_XOR | OpCode::SHL | OpCode::SHR
|
||||
| OpCode::USHR | OpCode::LT | OpCode::GT | OpCode::LTE | OpCode::GTE | OpCode::EQ
|
||||
| OpCode::NEQ | OpCode::STRICT_EQ | OpCode::STRICT_NEQ => match (inst.a, inst.b) {
|
||||
(Operand::Reg(x), Operand::Reg(y)) => match (
|
||||
known.get(x as usize).and_then(|c| c.as_ref()),
|
||||
known.get(y as usize).and_then(|c| c.as_ref()),
|
||||
) {
|
||||
(Some(l), Some(r)) => fold_table::fold_c_group(inst.op, l, r),
|
||||
_ => None,
|
||||
},
|
||||
_ => None,
|
||||
},
|
||||
_ => None,
|
||||
};
|
||||
|
||||
@@ -156,7 +178,8 @@ fn const_eq(a: &Constant, b: &Constant) -> bool {
|
||||
}
|
||||
|
||||
/// 新常量入池:线性扫描判等(命中复用既有下标);未命中推尾取新下标;池满
|
||||
/// (`u16::MAX`)时返回 `None` 禁用 A 组改写(B 组 MOV 不新增常量,不受影响)。
|
||||
/// (`u16::MAX`)时返回 `None` 禁用 A 组与 C 组改写(不置 `const_overflow`,
|
||||
/// 程序在 pass 前是有效的;B 组 MOV 不新增常量,不受影响)。
|
||||
fn intern(constants: &mut Vec<Constant>, c: &Constant) -> Option<u16> {
|
||||
if let Some(idx) = constants.iter().position(|x| const_eq(x, c)) {
|
||||
return Some(idx as u16);
|
||||
|
||||
@@ -1,10 +1,13 @@
|
||||
//! 常量折叠 pass 集成测试:手工构造 IR,断言前扫后指令流与常量池形态。
|
||||
//!
|
||||
//! 覆盖四类行为:
|
||||
//! 覆盖五类行为:
|
||||
//! - 常量传播(LOAD_CONST 与 MOV 的 known 表流动)
|
||||
//! - 折叠(A 组一元与算术改写为 LOAD_CONST,B 组逻辑短路改写为 MOV)
|
||||
//! - 折叠(A 组一元与算术改写为 LOAD_CONST,C 组位运算与比较改写为
|
||||
//! LOAD_CONST,B 组逻辑短路改写为 MOV)
|
||||
//! - 汇合点(label 目标整体清空 known,跨汇合不折)
|
||||
//! - 负零守卫(`-0.0 + 0.0` 得 `+0.0`,不取恒等捷径)
|
||||
//! - 池溢出守卫(池满时 A 组与 C 组不折、B 组照折;`const_overflow`
|
||||
//! 置位时整个 pass 跳过)
|
||||
|
||||
use oxide_bytecode::module::Constant;
|
||||
use oxide_bytecode::opcode::OpCode;
|
||||
@@ -506,6 +509,200 @@ fn negative_zero_neg_yields_positive_zero() {
|
||||
}
|
||||
}
|
||||
|
||||
// ── 折叠:C 组位运算 ──
|
||||
|
||||
#[test]
|
||||
fn fold_bit_and_or_xor() {
|
||||
// 12 & 10 = 8 / 12 | 10 = 14 / 12 ^ 10 = 6,均恒 Int。
|
||||
let (a, b) = (Constant::Int(12), Constant::Int(10));
|
||||
let mut f = func(
|
||||
&[a, b],
|
||||
vec![
|
||||
Inst::load_const(Operand::Reg(1), 0),
|
||||
Inst::load_const(Operand::Reg(2), 1),
|
||||
Inst::new(OpCode::BIT_AND, Operand::Reg(3), Operand::Reg(1), Operand::Reg(2)),
|
||||
Inst::new(OpCode::BIT_OR, Operand::Reg(4), Operand::Reg(1), Operand::Reg(2)),
|
||||
Inst::new(OpCode::BIT_XOR, Operand::Reg(5), Operand::Reg(1), Operand::Reg(2)),
|
||||
],
|
||||
);
|
||||
fold(&mut f);
|
||||
assert_load_const(&f.insts[2], 3, 2);
|
||||
assert_eq!(f.constants[2], Constant::Int(8));
|
||||
assert_load_const(&f.insts[3], 4, 3);
|
||||
assert_eq!(f.constants[3], Constant::Int(14));
|
||||
assert_load_const(&f.insts[4], 5, 4);
|
||||
assert_eq!(f.constants[4], Constant::Int(6));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn fold_shifts() {
|
||||
// 1 << 34 = 1 << 2 = 4(移位量与 0x1F 掩码)/
|
||||
// -2147483648 >> 1 = -1073741824(算术右移)/
|
||||
// -1 >>> 0 = 4294967295(结果超 i32::MAX,升 Number)。
|
||||
let (a, b) = (Constant::Int(1), Constant::Int(34));
|
||||
let (c, d) = (Constant::Int(i32::MIN), Constant::Int(1));
|
||||
let (e, f2) = (Constant::Int(-1), Constant::Int(0));
|
||||
let mut f = func(
|
||||
&[a, b, c, d, e, f2],
|
||||
vec![
|
||||
Inst::load_const(Operand::Reg(1), 0),
|
||||
Inst::load_const(Operand::Reg(2), 1),
|
||||
Inst::new(OpCode::SHL, Operand::Reg(3), Operand::Reg(1), Operand::Reg(2)),
|
||||
Inst::load_const(Operand::Reg(4), 2),
|
||||
Inst::load_const(Operand::Reg(5), 3),
|
||||
Inst::new(OpCode::SHR, Operand::Reg(6), Operand::Reg(4), Operand::Reg(5)),
|
||||
Inst::load_const(Operand::Reg(7), 4),
|
||||
Inst::load_const(Operand::Reg(8), 5),
|
||||
Inst::new(OpCode::USHR, Operand::Reg(9), Operand::Reg(7), Operand::Reg(8)),
|
||||
],
|
||||
);
|
||||
fold(&mut f);
|
||||
assert_load_const(&f.insts[2], 3, 6);
|
||||
assert_eq!(f.constants[6], Constant::Int(4));
|
||||
assert_load_const(&f.insts[5], 6, 7);
|
||||
assert_eq!(f.constants[7], Constant::Int(-1_073_741_824));
|
||||
assert_load_const(&f.insts[8], 9, 8);
|
||||
assert_eq!(f.constants[8], Constant::Number(4_294_967_295.0));
|
||||
}
|
||||
|
||||
// ── 折叠:C 组比较 ──
|
||||
|
||||
#[test]
|
||||
fn fold_comparisons() {
|
||||
// 1 < 2 = true / 1 > 2 = false / 1 <= 1 = true / 2 >= 3 = false /
|
||||
// 1 == 1.0 = true / 1 != 2 = true / 1 === 1.0 = true / 1 !== 2 = true /
|
||||
// NaN < 1 = false(关系比较对 NaN 返回 None,归 false)。
|
||||
let (a, b, c, d) = (Constant::Int(1), Constant::Int(2), Constant::Number(1.0), Constant::Int(3));
|
||||
let (e, f2) = (Constant::Number(f64::NAN), Constant::Int(1));
|
||||
let mut f = func(
|
||||
&[a, b, c, d, e, f2],
|
||||
vec![
|
||||
Inst::load_const(Operand::Reg(1), 0),
|
||||
Inst::load_const(Operand::Reg(2), 1),
|
||||
Inst::new(OpCode::LT, Operand::Reg(3), Operand::Reg(1), Operand::Reg(2)),
|
||||
Inst::new(OpCode::GT, Operand::Reg(4), Operand::Reg(1), Operand::Reg(2)),
|
||||
Inst::load_const(Operand::Reg(5), 0),
|
||||
Inst::new(OpCode::LTE, Operand::Reg(6), Operand::Reg(5), Operand::Reg(1)),
|
||||
Inst::load_const(Operand::Reg(7), 1),
|
||||
Inst::load_const(Operand::Reg(8), 3),
|
||||
Inst::new(OpCode::GTE, Operand::Reg(9), Operand::Reg(7), Operand::Reg(8)),
|
||||
Inst::load_const(Operand::Reg(10), 2),
|
||||
Inst::new(OpCode::EQ, Operand::Reg(11), Operand::Reg(1), Operand::Reg(10)),
|
||||
Inst::new(OpCode::NEQ, Operand::Reg(12), Operand::Reg(1), Operand::Reg(2)),
|
||||
Inst::new(OpCode::STRICT_EQ, Operand::Reg(13), Operand::Reg(1), Operand::Reg(10)),
|
||||
Inst::new(OpCode::STRICT_NEQ, Operand::Reg(14), Operand::Reg(1), Operand::Reg(2)),
|
||||
Inst::load_const(Operand::Reg(15), 4),
|
||||
Inst::new(OpCode::LT, Operand::Reg(16), Operand::Reg(15), Operand::Reg(1)),
|
||||
],
|
||||
);
|
||||
fold(&mut f);
|
||||
// 池原有 6 条:Boolean(true) 首落位下标 6、Boolean(false) 下标 7,
|
||||
// 后续同值折叠复用。
|
||||
assert_load_const(&f.insts[2], 3, 6);
|
||||
assert_eq!(f.constants[6], Constant::Boolean(true));
|
||||
assert_load_const(&f.insts[3], 4, 7);
|
||||
assert_eq!(f.constants[7], Constant::Boolean(false));
|
||||
assert_load_const(&f.insts[5], 6, 6);
|
||||
assert_load_const(&f.insts[8], 9, 7);
|
||||
assert_load_const(&f.insts[10], 11, 6);
|
||||
assert_load_const(&f.insts[11], 12, 6);
|
||||
assert_load_const(&f.insts[12], 13, 6);
|
||||
assert_load_const(&f.insts[13], 14, 6);
|
||||
assert_load_const(&f.insts[15], 16, 7);
|
||||
assert_eq!(f.constants.len(), 8, "同值折叠应复用池下标");
|
||||
}
|
||||
|
||||
// ── C 组操作数域守卫(BigInt/String/Undefined 不折) ──
|
||||
|
||||
#[test]
|
||||
fn c_group_bigint_operand_not_folded() {
|
||||
// 1n & 1 → 不折(BigInt 操作数不折,运行期抛 TypeError)。
|
||||
let (a, b) = (Constant::BigInt(num_bigint::BigInt::from(1)), Constant::Int(1));
|
||||
let mut f = func(
|
||||
&[a, b],
|
||||
vec![
|
||||
Inst::load_const(Operand::Reg(1), 0),
|
||||
Inst::load_const(Operand::Reg(2), 1),
|
||||
Inst::new(OpCode::BIT_AND, Operand::Reg(3), Operand::Reg(1), Operand::Reg(2)),
|
||||
],
|
||||
);
|
||||
assert!(!fold(&mut f), "BigInt 操作数不应折叠");
|
||||
assert_eq!(f.insts[2].op, OpCode::BIT_AND);
|
||||
assert_eq!(f.constants.len(), 2, "不应新增常量");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn c_group_string_operand_not_folded() {
|
||||
// "1" < "2" → 不折(String 操作数不折,字符串比较路径为后续任务)。
|
||||
let (a, b) = (Constant::String("1".to_string()), Constant::String("2".to_string()));
|
||||
let mut f = func(
|
||||
&[a, b],
|
||||
vec![
|
||||
Inst::load_const(Operand::Reg(1), 0),
|
||||
Inst::load_const(Operand::Reg(2), 1),
|
||||
Inst::new(OpCode::LT, Operand::Reg(3), Operand::Reg(1), Operand::Reg(2)),
|
||||
],
|
||||
);
|
||||
assert!(!fold(&mut f), "String 操作数不应折叠");
|
||||
assert_eq!(f.insts[2].op, OpCode::LT);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn c_group_undefined_operand_not_folded() {
|
||||
// 1 < undefined → 不折(Undefined 操作数不折,与共享表四臂口径一致)。
|
||||
let (a, b) = (Constant::Int(1), Constant::Undefined);
|
||||
let mut f = func(
|
||||
&[a, b],
|
||||
vec![
|
||||
Inst::load_const(Operand::Reg(1), 0),
|
||||
Inst::load_const(Operand::Reg(2), 1),
|
||||
Inst::new(OpCode::LT, Operand::Reg(3), Operand::Reg(1), Operand::Reg(2)),
|
||||
],
|
||||
);
|
||||
assert!(!fold(&mut f), "Undefined 操作数不应折叠");
|
||||
assert_eq!(f.insts[2].op, OpCode::LT);
|
||||
}
|
||||
|
||||
// ── 池溢出守卫 ──
|
||||
|
||||
#[test]
|
||||
fn pool_full_disables_a_and_c_groups() {
|
||||
// 池 65535 条(Int(0)..Int(65534))即满:NEG(A 组)与 EQ(C 组)折值
|
||||
// 不在池内且无空间推尾 → 不折;AND(B 组)MOV 不新增常量 → 照折。
|
||||
let mut f = IRFunction::new();
|
||||
f.constants = (0..65535).map(Constant::Int).collect();
|
||||
f.insts = vec![
|
||||
Inst::load_const(Operand::Reg(1), 0),
|
||||
Inst::load_const(Operand::Reg(2), 65534),
|
||||
Inst::new(OpCode::NEG, Operand::Reg(3), Operand::Reg(1), Operand::None),
|
||||
Inst::new(OpCode::EQ, Operand::Reg(4), Operand::Reg(1), Operand::Reg(2)),
|
||||
Inst::new(OpCode::AND, Operand::Reg(5), Operand::Reg(1), Operand::Reg(2)),
|
||||
];
|
||||
fold(&mut f);
|
||||
assert_eq!(f.insts[2].op, OpCode::NEG, "池满时 A 组不应折叠");
|
||||
assert_eq!(f.insts[3].op, OpCode::EQ, "池满时 C 组不应折叠");
|
||||
assert_mov(&f.insts[4], 5, 1);
|
||||
assert_eq!(f.constants.len(), 65535, "池满时不应新增常量");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn const_overflow_skips_pass() {
|
||||
// const_overflow 已置位(emit 层):整个 pass 跳过,不改写。
|
||||
let (a, b) = (Constant::Int(1), Constant::Int(2));
|
||||
let mut f = func(
|
||||
&[a, b],
|
||||
vec![
|
||||
Inst::load_const(Operand::Reg(1), 0),
|
||||
Inst::load_const(Operand::Reg(2), 1),
|
||||
Inst::new(OpCode::ADD, Operand::Reg(3), Operand::Reg(1), Operand::Reg(2)),
|
||||
],
|
||||
);
|
||||
f.const_overflow = true;
|
||||
assert!(!fold(&mut f), "const_overflow 置位时不应改写");
|
||||
assert_eq!(f.insts[2].op, OpCode::ADD);
|
||||
assert_eq!(f.constants.len(), 2);
|
||||
}
|
||||
|
||||
// ── 确定性 ──
|
||||
|
||||
#[test]
|
||||
@@ -532,3 +729,31 @@ fn fold_is_deterministic() {
|
||||
assert_eq!(fingerprint(&g), first, "折叠结果应确定");
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn c_group_fold_is_deterministic() {
|
||||
// 同一源(C 组位运算加比较)连续折叠八次,指令流与常量池指纹全等。
|
||||
let build = || {
|
||||
func(
|
||||
&[Constant::Int(12), Constant::Int(10), Constant::Int(-1), Constant::Int(0)],
|
||||
vec![
|
||||
Inst::load_const(Operand::Reg(1), 0),
|
||||
Inst::load_const(Operand::Reg(2), 1),
|
||||
Inst::new(OpCode::BIT_XOR, Operand::Reg(3), Operand::Reg(1), Operand::Reg(2)),
|
||||
Inst::load_const(Operand::Reg(4), 2),
|
||||
Inst::load_const(Operand::Reg(5), 3),
|
||||
Inst::new(OpCode::USHR, Operand::Reg(6), Operand::Reg(4), Operand::Reg(5)),
|
||||
Inst::new(OpCode::LT, Operand::Reg(7), Operand::Reg(3), Operand::Reg(6)),
|
||||
],
|
||||
)
|
||||
};
|
||||
let mut f = build();
|
||||
fold(&mut f);
|
||||
let fingerprint = |f: &IRFunction| (f.insts.clone(), f.constants.clone());
|
||||
let first = fingerprint(&f);
|
||||
for _ in 0..7 {
|
||||
let mut g = build();
|
||||
fold(&mut g);
|
||||
assert_eq!(fingerprint(&g), first, "折叠结果应确定");
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user