perf: 逐指令活集拍平为单一位集加消费方维度守卫同步

This commit is contained in:
2026-10-12 12:18:10 +00:00
parent 215c74fe0b
commit 3cabfb70f9
12 changed files with 92 additions and 71 deletions
+5 -4
View File
@@ -32,9 +32,10 @@ pub fn encode_call_window(f: &mut IRFunction, live: &LiveInfo) {
.insts
.iter()
.any(|i| matches!(i.op, OpCode::TRY_BEGIN | OpCode::TRY_FINALLY_BEGIN));
// LiveInfo 维度守卫:与 alloc 相同纪律,过期则重算。维度匹配时借用而非复制
// (LiveInfo 稠密矩阵全量 clone 只读数据无收益)。
let live = if live.inst_live_before.len() == f.insts.len() && !f.insts.is_empty() {
// LiveInfo 维度守卫:与 alloc 相同纪律,过期则重算。拍平后外层长度 = N×words,
// 空 LiveInfo 的 words 为 0(0==N*0 会误判对齐),须同时判 words > 0。
// 维度匹配时借用而非复制(LiveInfo 稠密矩阵全量 clone 只读数据无收益)。
let live = if live.inst_live_before.len() == f.insts.len() * live.words && live.words > 0 && !f.insts.is_empty() {
Cow::Borrowed(live)
} else {
let cfg = oxide_cfg::build_cfg(f);
@@ -45,7 +46,7 @@ pub fn encode_call_window(f: &mut IRFunction, live: &LiveInfo) {
if !has_try && !f.is_generator && !f.is_async {
for (i, inst) in f.insts.iter_mut().enumerate() {
if matches!(inst.op, OpCode::CALL | OpCode::NEW_EXPRESSION | OpCode::SUPER_CALL) {
let after = &live.inst_live_after[i];
let after = &live.inst_live_after[i * live.words..(i + 1) * live.words];
// 存活上界 = 最大存活槽号 + 1;254/255 由帧单独保存,不进窗口。
// 按 u64 位集逐字取置位(活集小,仅遍历实际存活的槽)。
let mut upper = 0u32;
+4 -4
View File
@@ -106,8 +106,8 @@ pub(super) fn collect_real_vregs(f: &IRFunction) -> Vec<u32> {
/// spill_set 中的 vreg 不再是节点(其 fresh 替代品入图);fresh 按 at==i 并入该指令活集。
pub(super) fn build(f: &IRFunction, live: &LiveInfo, spill_set: &[u32], fresh: &[FreshVreg]) -> InterferenceGraph {
let mut real = collect_real_vregs(f);
// inst_live_before 长度 = 指令数(逐指令活集索引)
let inst_count = live.inst_live_before.len();
// inst_live_before 拍平后长度 = 指令数 × words,指令数由 len/words 还原(words 恒 ≥1)
let inst_count = live.inst_live_before.len() / live.words;
// 窗口只为参数连续性 MOV 桥预留;spread 调用实参经 ext 逐个读寄存器,无连续性要求。
let max_nargs = f
@@ -270,7 +270,7 @@ pub(super) fn build(f: &IRFunction, live: &LiveInfo, spill_set: &[u32], fresh: &
// 替代逐 node 全扫描——活集小(典型 2-4)时每指令从 O(nodes) 降到 O(live)。
// 升序遍历保证加边顺序与原 node 表升序扫描逐字节一致(确定性不漂移)。
let mut at_i: Vec<u32> = Vec::new();
let before = &live.inst_live_before[i];
let before = &live.inst_live_before[i * live.words..(i + 1) * live.words];
for (word_idx, word) in before.iter().enumerate() {
let mut bits = *word;
while bits != 0 {
@@ -298,7 +298,7 @@ pub(super) fn build(f: &IRFunction, live: &LiveInfo, spill_set: &[u32], fresh: &
// 寄存器同色会覆盖其值(elision 解构返回值被覆盖),须补此干涉边。
if let Some(d) = f.insts[i].def_reg() {
if oxide_liveness::bitset_get(&is_node, d as usize) {
let after = &live.inst_live_after[i];
let after = &live.inst_live_after[i * live.words..(i + 1) * live.words];
for (word_idx, word) in after.iter().enumerate() {
let mut bits = *word;
while bits != 0 {
+4 -3
View File
@@ -48,9 +48,10 @@ pub fn alloc(f: &mut IRFunction, live: &LiveInfo) -> Result<(), String> {
return Ok(());
}
// LiveInfo 维度守卫:删改后的过期 LiveInfo 索引错位会误删活指令。
// 维度匹配时借用而非复制——LiveInfo 是稠密 bitset 矩阵(大函数可达数百 MB),
// 全量 clone 白白复制一次只读数据。
let live = if live.inst_live_before.len() == f.insts.len() && !f.insts.is_empty() {
// 拍平后外层长度 = N×words,空 LiveInfo 的 words 为 0(0==N*0 会误判对齐),
// 须同时判 words > 0。维度匹配时借用而非复制——LiveInfo 是稠密 bitset 矩阵
// (大函数可达数百 MB),全量 clone 白白复制一次只读数据。
let live = if live.inst_live_before.len() == f.insts.len() * live.words && live.words > 0 && !f.insts.is_empty() {
Cow::Borrowed(live)
} else {
let cfg = oxide_cfg::build_cfg(f);