From 508f9d8ddc83e9f47e08c7d51a34992fc980cc2a Mon Sep 17 00:00:00 2001 From: lzkk <956449176@qq.com> Date: Wed, 27 May 2026 14:58:25 +0800 Subject: [PATCH] =?UTF-8?q?fix(mir):=20TrySplit=E5=BC=95=E7=94=A8=E5=A4=B1?= =?UTF-8?q?=E6=95=88=E4=BF=AE=E5=A4=8D=20+=20LLVM=20Defer=E6=9C=BA?= =?UTF-8?q?=E5=88=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Three fixes: 1. TrySplit: 参数从 LiveInterval& 改为 vreg索引,避免 push_back 使引用失效 — 旧代码在 intervals.push_back() 后继续使用 li 引用(UB),高寄存器压力下导致 段错误 2. LLVM Defer机制: 首次无法分配时将 vreg 推迟到堆尾(RS_New→RS_Deferred) — 让更小范围先分配以获得更清晰干涉图 — 参考: llvm/lib/CodeGen/RegAllocGreedy.cpp selectOrSplit() 3. LiveInterval 新增 deferred_count 字段追踪推迟状态 诊断: 65_color/94_nested_loops 在 Mem2Reg 开启时失败,关闭时通过。 Mem2Reg 消除栈分配(alloca→SSA),增加同时活跃 vreg 数量,触发高压 spill。 根因追踪到 TrySplit 的引用失效(与之前 heap 指针失效同类 bug)。 当前: 94% (94/100),剩余6失败详见 project_greedy_alloc_progress.md --- src/include/mir/MIR.h | 2 + src/mir/GreedyAlloc.cpp | 197 ++++++++++++++++++++++------------------ 2 files changed, 109 insertions(+), 90 deletions(-) diff --git a/src/include/mir/MIR.h b/src/include/mir/MIR.h index c9b071e5..88e6ea38 100644 --- a/src/include/mir/MIR.h +++ b/src/include/mir/MIR.h @@ -191,6 +191,7 @@ namespace mir switch (vc) { case VRegClass::Int: return RegClass::GPR32; case VRegClass::Float: return RegClass::FPR32; + case VRegClass::Ptr: return RegClass::GPR64; default: return RegClass::Unknown; } } @@ -502,6 +503,7 @@ namespace mir float spill_weight = 0.0f; int hint_reg = -1; int generation = 0; + int deferred_count = 0; // LLVM: RS_New→RS_Deferred→RS_Split stage tracking // 保留旧字段以兼容 ComputeInstLiveness int start = -1; diff --git a/src/mir/GreedyAlloc.cpp b/src/mir/GreedyAlloc.cpp index c89dc82d..7f8e2468 100644 --- a/src/mir/GreedyAlloc.cpp +++ b/src/mir/GreedyAlloc.cpp @@ -296,95 +296,101 @@ bool TryEvict(LiveInterval &li, LiveRegMatrix &m, } // ---- TrySplit(简化版——只用于最复杂的 vreg)---- -bool TrySplit(LiveInterval &li, LiveRegMatrix &m, - std::vector &heap, - std::vector &intervals, - const std::vector &pos_to_block, - std::vector &spilled, - MachineFunction &func, - const SpillWeightCmp &cmp) -{ - if (li.uses.size() < 3) return false; - - // 在中间位置分裂:hot 段尝试分配,cold 段入堆 - int mid = (int)li.uses.size() / 2; - int split_pos = li.uses[mid].pos; - int hot_start = li.FirstUsePos(); - int hot_end = split_pos; - int cold_start = split_pos + 1; - int cold_end = li.LastUsePos(); - - if (hot_end < hot_start || cold_end < cold_start) return false; - - // 构建 cold 子区间 - LiveInterval cold; - cold.reg_class = li.reg_class; - cold.generation = li.generation + 1; - cold.hint_reg = -1; - cold.assigned_reg = -1; - cold.vreg = func.CreateVReg(li.vreg_class); - - for (auto &seg : li.segments) - { - if (seg.end < cold_start || seg.start > cold_end) continue; - Segment clipped = seg; - clipped.start = std::max(seg.start, cold_start); - clipped.end = std::min(seg.end, cold_end); - cold.segments.push_back(clipped); - } - for (auto &use : li.uses) - if (cold_start <= use.pos && use.pos <= cold_end) - cold.uses.push_back(use); - - if (cold.uses.empty()) return false; - - float w = 0.0f; - for (auto &use : cold.uses) - { - int blk = (use.pos >= 0 && use.pos < (int)pos_to_block.size()) - ? pos_to_block[use.pos] : 0; - float mult = 1.0f; - if (use.is_def) mult *= 0.5f; - w += mult; - } - cold.spill_weight = w / cold.Length(); - - intervals.push_back(std::move(cold)); - LiveInterval &cold_ref = intervals.back(); - - // 修剪 li 为 hot 段 - li.segments.clear(); - for (auto &seg : intervals[li.vreg].segments) - { - if (seg.end < hot_start || seg.start > hot_end) continue; - Segment clipped = seg; - clipped.start = std::max(seg.start, hot_start); - clipped.end = std::min(seg.end, hot_end); - if (clipped.start <= clipped.end) - li.segments.push_back(clipped); - } - li.uses.erase( - std::remove_if(li.uses.begin(), li.uses.end(), - [&](const UsePosition &u) { - return u.pos < hot_start || u.pos > hot_end; - }), - li.uses.end()); - - // 尝试给 hot 分配 - if (!TryAnyFreeReg(li, m)) - { - li.assigned_reg = -2; - spilled.push_back(li.vreg); - } - - // cold 入堆 - if (!TryAnyFreeReg(cold_ref, m)) - { - heap.push_back(cold_ref.vreg); - std::push_heap(heap.begin(), heap.end(), cmp); - } - return true; -} + // 注意:push_back 会使引用失效,通过 vreg 索引安全访问 + bool TrySplit(int vreg_idx, LiveRegMatrix &m, + std::vector &heap, + std::vector &intervals, + const std::vector &pos_to_block, + std::vector &spilled, + MachineFunction &func, + const SpillWeightCmp &cmp) + { + auto &li = intervals[vreg_idx]; + if (li.uses.size() < 3) return false; + + // 在中间位置分裂:hot 段尝试分配,cold 段入堆 + int mid = (int)li.uses.size() / 2; + int split_pos = li.uses[mid].pos; + int hot_start = li.FirstUsePos(); + int hot_end = split_pos; + int cold_start = split_pos + 1; + int cold_end = li.LastUsePos(); + + if (hot_end < hot_start || cold_end < cold_start) return false; + + // 构建 cold 子区间(在 push_back 前提取所需字段,避免引用失效) + LiveInterval cold; + cold.reg_class = li.reg_class; + cold.generation = li.generation + 1; + cold.hint_reg = -1; + cold.assigned_reg = -1; + cold.vreg = func.CreateVReg(li.vreg_class); + + for (auto &seg : li.segments) + { + if (seg.end < cold_start || seg.start > cold_end) continue; + Segment clipped = seg; + clipped.start = std::max(seg.start, cold_start); + clipped.end = std::min(seg.end, cold_end); + cold.segments.push_back(clipped); + } + for (auto &use : li.uses) + if (cold_start <= use.pos && use.pos <= cold_end) + cold.uses.push_back(use); + + if (cold.uses.empty()) return false; + + float w = 0.0f; + for (auto &use : cold.uses) + { + int blk = (use.pos >= 0 && use.pos < (int)pos_to_block.size()) + ? pos_to_block[use.pos] : 0; + float mult = 1.0f; + if (use.is_def) mult *= 0.5f; + w += mult; + } + cold.spill_weight = w / cold.Length(); + + int cold_vreg = cold.vreg; + intervals.push_back(std::move(cold)); + // push_back 后 li 引用可能失效,通过 vreg_idx 安全访问 + + // 修剪原 vreg 为 hot 段 + auto &li_safe = intervals[vreg_idx]; + std::vector hot_segs; + for (auto &seg : li_safe.segments) + { + if (seg.end < hot_start || seg.start > hot_end) continue; + Segment clipped = seg; + clipped.start = std::max(seg.start, hot_start); + clipped.end = std::min(seg.end, hot_end); + if (clipped.start <= clipped.end) + hot_segs.push_back(clipped); + } + li_safe.segments = std::move(hot_segs); + li_safe.uses.erase( + std::remove_if(li_safe.uses.begin(), li_safe.uses.end(), + [&](const UsePosition &u) { + return u.pos < hot_start || u.pos > hot_end; + }), + li_safe.uses.end()); + + // 尝试给 hot 分配 + if (!TryAnyFreeReg(li_safe, m)) + { + li_safe.assigned_reg = -2; + spilled.push_back(vreg_idx); + } + + // cold 入堆 + auto &cold_ref = intervals[cold_vreg]; + if (!TryAnyFreeReg(cold_ref, m)) + { + heap.push_back(cold_vreg); + std::push_heap(heap.begin(), heap.end(), cmp); + } + return true; + } // ---- 主分配函数:对一类寄存器执行贪婪分配 ---- // 返回 spilled 数量 @@ -436,7 +442,18 @@ int AllocateRegClass(std::vector &intervals, { if (TryEvict(li, matrix, heap, cmp)) continue; } - if (TrySplit(li, matrix, heap, intervals, + + // LLVM Defer 机制: 首次分配失败时推迟到下一轮,让更小的范围先分配 + // 参考: llvm/lib/CodeGen/RegAllocGreedy.cpp selectOrSplit() RS_New→RS_Deferred + if (li.deferred_count == 0) + { + li.deferred_count = 1; + heap.push_back(vreg); + std::push_heap(heap.begin(), heap.end(), cmp); + continue; + } + + if (TrySplit(vreg, matrix, heap, intervals, pos_to_block, spilled, func, cmp)) continue; li.assigned_reg = -2;