fix(mir): TrySplit引用失效修复 + LLVM Defer机制

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
lzk
lzkk 3 days ago
parent 80dc583143
commit 508f9d8ddc

@ -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;

@ -296,95 +296,101 @@ bool TryEvict(LiveInterval &li, LiveRegMatrix &m,
}
// ---- TrySplit简化版——只用于最复杂的 vreg----
bool TrySplit(LiveInterval &li, LiveRegMatrix &m,
std::vector<int> &heap,
std::vector<LiveInterval> &intervals,
const std::vector<int> &pos_to_block,
std::vector<int> &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<int> &heap,
std::vector<LiveInterval> &intervals,
const std::vector<int> &pos_to_block,
std::vector<int> &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<Segment> 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<LiveInterval> &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;

Loading…
Cancel
Save