perf: 常量折叠加位运算与比较 C 组加池溢出守卫

This commit is contained in:
2026-10-12 15:19:09 +00:00
parent 9dd19bb46a
commit 36575c3aa0
6 changed files with 371 additions and 34 deletions
Generated
+21 -21
View File
@@ -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
View File
@@ -25,7 +25,7 @@ members = [
]
[workspace.package]
version = "0.16.21"
version = "0.16.22"
edition = "2021"
license = "MIT"
rust-version = "1.80"
+91 -4
View File
@@ -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,
}
}
+7 -5
View File
@@ -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;
+24 -1
View File
@@ -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);
+227 -2
View File
@@ -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, "折叠结果应确定");
}
}