fix(mir): LLVM两遍分配 + 间隙分裂 + Assign安全网

改进:
1. 两遍分配(Pass1短活范围优先→Pass2延迟vreg驱逐/分裂)
   — SSA弦图完美消除序近似:短范围先分配,长范围获得清晰干涉图
2. TrySplit改间隙分裂:在连续use间最大间隙处分裂,替代简单中点切分
   — 参考 LLVM tryLocalSplit()
3. Assign返回bool + ForceAssign(预填跳过检查)
   — 防止非法分配通过的安全网

诊断:Mem2Reg开启时,CheckInterference在某些情况下漏过重叠检测,
导致冲突vreg分配到同一物理寄存器。具体触发条件待进一步定位。

当前正确率:94% (94/100)
lzk
lzkk 6 days ago
parent 508f9d8ddc
commit 5fb106bde8

@ -547,7 +547,8 @@ namespace mir
public:
void Init(int num_regs);
void Assign(LiveInterval *li, int phys_reg);
bool Assign(LiveInterval *li, int phys_reg);
void ForceAssign(LiveInterval *li, int phys_reg);
void Unassign(LiveInterval *li);
bool CheckInterference(const LiveInterval &li, int phys_reg) const;
LiveInterval *GetConflict(const LiveInterval &li, int phys_reg) const;

@ -39,7 +39,9 @@ const int* GetRegList(RegClass rc, int& count)
// 堆排序:高 cascade已被驱逐过的永远排在低 cascade 之后;
// 同等 cascade 按 spill_weight 降序(堆顶权重最大,优先分配)。
// heap 存储 vreg 索引,避免 TrySplit 中 intervals.push_back 导致指针失效
// heap 存储 vreg 索引
// Stage 0 (new): 短活范围优先——弦图完美消除序近似
// Stage 1+ (deferred/evicted): spill_weight 降序
struct SpillWeightCmp
{
const std::vector<LiveInterval>& intervals;
@ -50,6 +52,8 @@ struct SpillWeightCmp
const auto& lb = intervals[b];
if (la.generation != lb.generation)
return la.generation > lb.generation;
if (la.deferred_count == 0 && lb.deferred_count == 0)
return la.Length() > lb.Length();
return la.spill_weight < lb.spill_weight;
}
};
@ -220,9 +224,8 @@ bool TryAssign(LiveInterval &li, LiveRegMatrix &m, int hint)
{
if (hint < 0) return false;
if (IsCallerSavedGP(hint) && li.SegmentCrossesCall()) return false;
if (!m.CheckInterference(li, hint))
if (!m.CheckInterference(li, hint) && m.Assign(&li, hint))
{
m.Assign(&li, hint);
li.assigned_reg = hint;
return true;
}
@ -238,9 +241,8 @@ bool TryAnyFreeReg(LiveInterval &li, LiveRegMatrix &m)
{
int r = regs[i];
if (IsCallerSavedGP(r) && li.SegmentCrossesCall()) continue;
if (!m.CheckInterference(li, r))
if (!m.CheckInterference(li, r) && m.Assign(&li, r))
{
m.Assign(&li, r);
li.assigned_reg = r;
return true;
}
@ -266,9 +268,8 @@ bool TryEvict(LiveInterval &li, LiveRegMatrix &m,
int r = regs[i];
if (IsCallerSavedGP(r) && li.SegmentCrossesCall()) continue;
auto *conflict = m.GetConflict(li, r);
if (!conflict)
if (!conflict && m.Assign(&li, r))
{
m.Assign(&li, r);
li.assigned_reg = r;
return true;
}
@ -290,13 +291,16 @@ bool TryEvict(LiveInterval &li, LiveRegMatrix &m,
heap.push_back(victim->vreg);
std::push_heap(heap.begin(), heap.end(), cmp);
m.Assign(&li, best_reg);
li.assigned_reg = best_reg;
return true;
if (m.Assign(&li, best_reg))
{
li.assigned_reg = best_reg;
return true;
}
return false;
}
// ---- TrySplit简化版——只用于最复杂的 vreg----
// 注意push_back 会使引用失效,通过 vreg 索引安全访问
// ---- TrySplit:在最大使用间隙处分裂LLVM local split 简化版)----
// 参考: llvm/lib/CodeGen/RegAllocGreedy.cpp tryLocalSplit()
bool TrySplit(int vreg_idx, LiveRegMatrix &m,
std::vector<int> &heap,
std::vector<LiveInterval> &intervals,
@ -308,26 +312,27 @@ bool TryEvict(LiveInterval &li, LiveRegMatrix &m,
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();
std::vector<int> sorted_uses;
for (auto &u : li.uses) sorted_uses.push_back(u.pos);
std::sort(sorted_uses.begin(), sorted_uses.end());
int best_gap = 0, split_after = -1;
for (size_t i = 1; i < sorted_uses.size(); ++i) {
int gap = sorted_uses[i] - sorted_uses[i - 1];
if (gap > best_gap && gap >= 2) { best_gap = gap; split_after = sorted_uses[i - 1]; }
}
if (split_after < 0) return false;
int hot_start = li.FirstUsePos(), hot_end = split_after;
int cold_start = split_after + 1, 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.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)
{
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);
@ -335,63 +340,37 @@ bool TryEvict(LiveInterval &li, LiveRegMatrix &m,
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_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;
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 = use.is_def ? 0.5f : 1.0f;
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)
{
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);
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);
}
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());
// cold 入堆
if (!TryAnyFreeReg(li_safe, m)) { li_safe.assigned_reg = -2; spilled.push_back(vreg_idx); }
auto &cold_ref = intervals[cold_vreg];
if (!TryAnyFreeReg(cold_ref, m))
{
heap.push_back(cold_vreg);
std::push_heap(heap.begin(), heap.end(), cmp);
}
if (!TryAnyFreeReg(cold_ref, m)) { heap.push_back(cold_vreg); std::push_heap(heap.begin(), heap.end(), cmp); }
return true;
}
// ---- 主分配函数:对一类寄存器执行贪婪分配 ----
// 返回 spilled 数量
int AllocateRegClass(std::vector<LiveInterval> &intervals,
@ -469,10 +448,17 @@ int AllocateRegClass(std::vector<LiveInterval> &intervals,
void LiveRegMatrix::Init(int num_regs)
{ reg_assignments_.assign(num_regs, {}); }
void LiveRegMatrix::Assign(LiveInterval *li, int phys_reg)
void LiveRegMatrix::ForceAssign(LiveInterval *li, int phys_reg)
{
if (phys_reg < 0 || phys_reg >= (int)reg_assignments_.size()) return;
if (phys_reg >= 0 && phys_reg < (int)reg_assignments_.size())
reg_assignments_[phys_reg].push_back(li);
}
bool LiveRegMatrix::Assign(LiveInterval *li, int phys_reg)
{
if (phys_reg < 0 || phys_reg >= (int)reg_assignments_.size()) return false;
reg_assignments_[phys_reg].push_back(li);
return true;
}
void LiveRegMatrix::Unassign(LiveInterval *li)
@ -581,7 +567,7 @@ static void AllocateRegistersForFunction(MachineFunction &function)
{
if (li.vreg >= 0 && li.IsAllocated() &&
(li.reg_class == RegClass::GPR32 || li.reg_class == RegClass::GPR64))
gp_matrix.Assign(&li, li.assigned_reg);
gp_matrix.ForceAssign(&li, li.assigned_reg);
}
AllocateRegClass(intervals, RegClass::GPR32, gp_matrix,
@ -597,7 +583,7 @@ static void AllocateRegistersForFunction(MachineFunction &function)
for (auto &li : intervals)
{
if (li.vreg >= 0 && li.IsAllocated() && li.reg_class == RegClass::FPR32)
fp_matrix.Assign(&li, li.assigned_reg);
fp_matrix.ForceAssign(&li, li.assigned_reg);
}
AllocateRegClass(intervals, RegClass::FPR32, fp_matrix,

Loading…
Cancel
Save