feat(ir): 添加 IR 验证器,校验 SSA 支配性/终结指令/PHI 一致性

- 新建 IRVerifier pass:检查非 PHI 指令的 SSA 支配性、基本块终结指令、
  PHI 操作数结构
- 提取 DominatorTree 类到独立头文件,供验证器复用
- User 新增 ClearOperands() 方法,用于重建操作数列表
- 修复 CFGSimplify 两处 PHI 清理遗漏:
  1. 常量条件分支简化后,死目标的 PHI 未移除原前驱条目
  2. 不可达块删除时,PHI 中部分不可达前驱条目未清理
- 验证器仅在 Debug 模式生效(#ifndef NDEBUG)
- 快门禁:functional 86/87 通过,h_functional 30/31 通过
  (1 例预置超时/段错误,非本次引入)
lzk
lzkk 1 week ago
parent ef6eedee83
commit 0b589c77da

@ -214,6 +214,8 @@ class User : public Value {
Value* GetOperand(size_t index) const;
void SetOperand(size_t index, Value* value);
void AddOperand(Value* value);
// 清除所有操作数并注销 use 记录,用于重建 PHI 等场景。
void ClearOperands();
private:
std::vector<Value*> operands_;
@ -542,4 +544,8 @@ class IRPrinter {
void Print(const Module& module, std::ostream& os);
};
// IR 验证器校验模块合法性Debug 模式)。
// 检查 SSA 支配性、基本块终结指令、PHI 一致性等。
void VerifyIR(Module &module);
} // namespace ir

@ -47,6 +47,15 @@ void User::AddOperand(Value* value) {
value->AddUse(this, operand_index);
}
void User::ClearOperands() {
for (size_t i = 0; i < operands_.size(); ++i) {
if (operands_[i]) {
operands_[i]->RemoveUse(this, i);
}
}
operands_.clear();
}
Instruction::Instruction(Opcode op, std::shared_ptr<Type> ty, std::string name)
: User(std::move(ty), std::move(name)), opcode_(op) {}

@ -1,3 +1,5 @@
#include "ir/analysis/DominatorTree.h"
#include "ir/IR.h"
#include <algorithm>
@ -5,313 +7,254 @@
#include <unordered_set>
#include <vector>
namespace ir
{
namespace ir {
namespace
{
namespace {
std::unordered_map<BasicBlock *, std::vector<BasicBlock *>> ComputePredecessors(Function *func)
{
std::unordered_map<BasicBlock *, std::vector<BasicBlock *>> preds;
for (const auto &bb : func->GetBlocks())
{
preds[bb.get()] = {};
}
for (const auto &bb : func->GetBlocks())
{
if (!bb->HasTerminator())
{
continue;
}
auto *terminator = bb->GetInstructions().back().get();
if (auto *br = dynamic_cast<BranchInst *>(terminator))
{
preds[br->GetTarget()].push_back(bb.get());
}
else if (auto *condbr = dynamic_cast<CondBranchInst *>(terminator))
{
preds[condbr->GetTrueTarget()].push_back(bb.get());
preds[condbr->GetFalseTarget()].push_back(bb.get());
}
}
return preds;
std::unordered_map<BasicBlock*, std::vector<BasicBlock*>> ComputePredecessors(
Function* func) {
std::unordered_map<BasicBlock*, std::vector<BasicBlock*>> preds;
for (const auto& bb : func->GetBlocks()) {
preds[bb.get()] = {};
}
for (const auto& bb : func->GetBlocks()) {
if (!bb->HasTerminator()) {
continue;
}
auto* terminator = bb->GetInstructions().back().get();
if (auto* br = dynamic_cast<BranchInst*>(terminator)) {
preds[br->GetTarget()].push_back(bb.get());
} else if (auto* condbr = dynamic_cast<CondBranchInst*>(terminator)) {
preds[condbr->GetTrueTarget()].push_back(bb.get());
preds[condbr->GetFalseTarget()].push_back(bb.get());
}
}
return preds;
}
std::unordered_map<BasicBlock *, std::vector<BasicBlock *>> ComputeSuccessors(Function *func)
{
std::unordered_map<BasicBlock *, std::vector<BasicBlock *>> succs;
for (const auto &bb : func->GetBlocks())
{
succs[bb.get()] = {};
if (!bb->HasTerminator())
{
continue;
}
auto *terminator = bb->GetInstructions().back().get();
if (auto *br = dynamic_cast<BranchInst *>(terminator))
{
succs[bb.get()].push_back(br->GetTarget());
}
else if (auto *condbr = dynamic_cast<CondBranchInst *>(terminator))
{
succs[bb.get()].push_back(condbr->GetTrueTarget());
succs[bb.get()].push_back(condbr->GetFalseTarget());
}
}
return succs;
std::unordered_map<BasicBlock*, std::vector<BasicBlock*>> ComputeSuccessors(
Function* func) {
std::unordered_map<BasicBlock*, std::vector<BasicBlock*>> succs;
for (const auto& bb : func->GetBlocks()) {
succs[bb.get()] = {};
if (!bb->HasTerminator()) {
continue;
}
auto* terminator = bb->GetInstructions().back().get();
if (auto* br = dynamic_cast<BranchInst*>(terminator)) {
succs[bb.get()].push_back(br->GetTarget());
} else if (auto* condbr = dynamic_cast<CondBranchInst*>(terminator)) {
succs[bb.get()].push_back(condbr->GetTrueTarget());
succs[bb.get()].push_back(condbr->GetFalseTarget());
}
}
return succs;
}
std::vector<BasicBlock *> PostOrder(Function *func,
const std::unordered_map<BasicBlock *, std::vector<BasicBlock *>> &succs)
{
std::vector<BasicBlock *> order;
std::unordered_set<BasicBlock *> visited;
std::vector<std::pair<BasicBlock *, size_t>> stack;
auto *entry = func->GetEntry();
if (!entry)
{
return order;
}
std::vector<BasicBlock*> PostOrder(
Function* func,
const std::unordered_map<BasicBlock*, std::vector<BasicBlock*>>& succs) {
std::vector<BasicBlock*> order;
std::unordered_set<BasicBlock*> visited;
std::vector<std::pair<BasicBlock*, size_t>> stack;
stack.push_back({entry, 0});
visited.insert(entry);
while (!stack.empty())
{
auto &top = stack.back();
auto *bb = top.first;
auto &idx = top.second;
auto it = succs.find(bb);
const auto &children = (it != succs.end()) ? it->second : std::vector<BasicBlock *>{};
bool found_next = false;
while (idx < children.size())
{
auto *child = children[idx++];
if (visited.find(child) == visited.end())
{
visited.insert(child);
stack.push_back({child, 0});
found_next = true;
break;
}
}
auto* entry = func->GetEntry();
if (!entry) {
return order;
}
if (!found_next)
{
order.push_back(bb);
stack.pop_back();
}
stack.push_back({entry, 0});
visited.insert(entry);
while (!stack.empty()) {
auto& top = stack.back();
auto* bb = top.first;
auto& idx = top.second;
auto it = succs.find(bb);
const auto& children =
(it != succs.end()) ? it->second : std::vector<BasicBlock*>{};
bool found_next = false;
while (idx < children.size()) {
auto* child = children[idx++];
if (visited.find(child) == visited.end()) {
visited.insert(child);
stack.push_back({child, 0});
found_next = true;
break;
}
}
return order;
if (!found_next) {
order.push_back(bb);
stack.pop_back();
}
}
std::unordered_set<BasicBlock *> Intersect(const std::unordered_set<BasicBlock *> &a,
const std::unordered_set<BasicBlock *> &b)
{
std::unordered_set<BasicBlock *> result;
for (auto *bb : a)
{
if (b.find(bb) != b.end())
{
result.insert(bb);
}
}
return result;
return order;
}
std::unordered_set<BasicBlock*> Intersect(
const std::unordered_set<BasicBlock*>& a,
const std::unordered_set<BasicBlock*>& b) {
std::unordered_set<BasicBlock*> result;
for (auto* bb : a) {
if (b.find(bb) != b.end()) {
result.insert(bb);
}
}
return result;
}
} // namespace
void DominatorTree::Compute(Function* func) {
doms_.clear();
idom_.clear();
children_.clear();
df_.clear();
auto preds = ComputePredecessors(func);
auto succs = ComputeSuccessors(func);
auto post_order = PostOrder(func, succs);
std::unordered_map<BasicBlock*, size_t> post_order_idx;
for (size_t i = 0; i < post_order.size(); ++i) {
post_order_idx[post_order[i]] = i;
}
class DominatorTree
{
public:
void Compute(Function *func)
{
doms_.clear();
idom_.clear();
children_.clear();
df_.clear();
auto preds = ComputePredecessors(func);
auto succs = ComputeSuccessors(func);
auto post_order = PostOrder(func, succs);
std::unordered_map<BasicBlock *, size_t> post_order_idx;
for (size_t i = 0; i < post_order.size(); ++i)
{
post_order_idx[post_order[i]] = i;
}
auto* entry = func->GetEntry();
if (!entry) {
return;
}
auto *entry = func->GetEntry();
if (!entry)
{
return;
}
std::unordered_set<BasicBlock*> all_blocks;
for (const auto& bb : func->GetBlocks()) {
all_blocks.insert(bb.get());
}
std::unordered_set<BasicBlock *> all_blocks;
for (const auto &bb : func->GetBlocks())
{
all_blocks.insert(bb.get());
}
doms_[entry] = {entry};
for (auto* bb : post_order) {
if (bb != entry) {
doms_[bb] = all_blocks;
}
}
doms_[entry] = {entry};
for (auto *bb : post_order)
{
if (bb != entry)
{
doms_[bb] = all_blocks;
}
bool changed = true;
while (changed) {
changed = false;
for (auto it = post_order.rbegin(); it != post_order.rend(); ++it) {
auto* bb = *it;
if (bb == entry) {
continue;
}
bool changed = true;
while (changed)
{
changed = false;
for (auto it = post_order.rbegin(); it != post_order.rend(); ++it)
{
auto *bb = *it;
if (bb == entry)
{
continue;
}
auto pred_it = preds.find(bb);
if (pred_it == preds.end() || pred_it->second.empty())
{
continue;
}
std::unordered_set<BasicBlock *> new_dom;
bool first = true;
for (auto *pred : pred_it->second)
{
auto dom_it = doms_.find(pred);
if (dom_it == doms_.end())
{
continue;
}
if (first)
{
new_dom = dom_it->second;
first = false;
}
else
{
new_dom = Intersect(new_dom, dom_it->second);
}
}
new_dom.insert(bb);
if (doms_[bb] != new_dom)
{
doms_[bb] = new_dom;
changed = true;
}
}
auto pred_it = preds.find(bb);
if (pred_it == preds.end() || pred_it->second.empty()) {
continue;
}
for (auto *bb : post_order)
{
if (bb == entry)
{
idom_[bb] = nullptr;
std::unordered_set<BasicBlock*> new_dom;
bool first = true;
for (auto* pred : pred_it->second) {
auto dom_it = doms_.find(pred);
if (dom_it == doms_.end()) {
continue;
}
auto &dom_set = doms_[bb];
BasicBlock *idom = nullptr;
size_t min_idx = post_order.size();
for (auto *d : dom_set)
{
if (d == bb)
{
continue;
}
auto idx_it = post_order_idx.find(d);
if (idx_it != post_order_idx.end() && idx_it->second < min_idx)
{
min_idx = idx_it->second;
idom = d;
}
if (first) {
new_dom = dom_it->second;
first = false;
} else {
new_dom = Intersect(new_dom, dom_it->second);
}
}
new_dom.insert(bb);
idom_[bb] = idom;
if (idom)
{
children_[idom].push_back(bb);
}
if (doms_[bb] != new_dom) {
doms_[bb] = new_dom;
changed = true;
}
}
}
for (auto *bb : post_order)
{
if (bb == entry)
{
continue;
}
for (auto* bb : post_order) {
if (bb == entry) {
idom_[bb] = nullptr;
continue;
}
auto pred_it = preds.find(bb);
if (pred_it == preds.end())
{
continue;
}
auto& dom_set = doms_[bb];
BasicBlock* idom = nullptr;
size_t min_idx = post_order.size();
for (auto *pred : pred_it->second)
{
auto *runner = pred;
while (runner && runner != idom_[bb])
{
df_[runner].insert(bb);
runner = idom_[runner];
}
}
for (auto* d : dom_set) {
if (d == bb) {
continue;
}
}
bool Dominates(BasicBlock *a, BasicBlock *b) const
{
auto it = doms_.find(b);
if (it == doms_.end())
{
return false;
auto idx_it = post_order_idx.find(d);
if (idx_it != post_order_idx.end() && idx_it->second < min_idx) {
min_idx = idx_it->second;
idom = d;
}
return it->second.find(a) != it->second.end();
}
BasicBlock *GetIdom(BasicBlock *bb) const
{
auto it = idom_.find(bb);
return (it != idom_.end()) ? it->second : nullptr;
idom_[bb] = idom;
if (idom) {
children_[idom].push_back(bb);
}
}
const std::vector<BasicBlock *> &GetChildren(BasicBlock *bb) const
{
static const std::vector<BasicBlock *> empty;
auto it = children_.find(bb);
return (it != children_.end()) ? it->second : empty;
for (auto* bb : post_order) {
if (bb == entry) {
continue;
}
const std::unordered_set<BasicBlock *> &GetDominanceFrontier(BasicBlock *bb) const
{
static const std::unordered_set<BasicBlock *> empty;
auto it = df_.find(bb);
return (it != df_.end()) ? it->second : empty;
auto pred_it = preds.find(bb);
if (pred_it == preds.end()) {
continue;
}
const std::unordered_map<BasicBlock *, std::unordered_set<BasicBlock *>> &GetAllDominanceFrontiers() const
{
return df_;
for (auto* pred : pred_it->second) {
auto* runner = pred;
while (runner && runner != idom_[bb]) {
df_[runner].insert(bb);
runner = idom_[runner];
}
}
}
}
private:
std::unordered_map<BasicBlock *, std::unordered_set<BasicBlock *>> doms_;
std::unordered_map<BasicBlock *, BasicBlock *> idom_;
std::unordered_map<BasicBlock *, std::vector<BasicBlock *>> children_;
std::unordered_map<BasicBlock *, std::unordered_set<BasicBlock *>> df_;
};
bool DominatorTree::Dominates(BasicBlock* a, BasicBlock* b) const {
auto it = doms_.find(b);
if (it == doms_.end()) {
return false;
}
return it->second.find(a) != it->second.end();
}
BasicBlock* DominatorTree::GetIdom(BasicBlock* bb) const {
auto it = idom_.find(bb);
return (it != idom_.end()) ? it->second : nullptr;
}
const std::vector<BasicBlock*>& DominatorTree::GetChildren(
BasicBlock* bb) const {
static const std::vector<BasicBlock*> empty;
auto it = children_.find(bb);
return (it != children_.end()) ? it->second : empty;
}
const std::unordered_set<BasicBlock*>& DominatorTree::GetDominanceFrontier(
BasicBlock* bb) const {
static const std::unordered_set<BasicBlock*> empty;
auto it = df_.find(bb);
return (it != df_.end()) ? it->second : empty;
}
const std::unordered_map<BasicBlock*, std::unordered_set<BasicBlock*>>&
DominatorTree::GetAllDominanceFrontiers() const {
return df_;
}
} // namespace ir

@ -0,0 +1,37 @@
// 支配树:计算函数的支配关系、直接支配者、支配边界。
// 核心算法是迭代数据流分析,用于 IR 验证器、Mem2Reg 等 pass。
#pragma once
#include <unordered_map>
#include <unordered_set>
#include <vector>
namespace ir {
class BasicBlock;
class Function;
class DominatorTree {
public:
void Compute(Function* func);
bool Dominates(BasicBlock* a, BasicBlock* b) const;
BasicBlock* GetIdom(BasicBlock* bb) const;
const std::vector<BasicBlock*>& GetChildren(BasicBlock* bb) const;
const std::unordered_set<BasicBlock*>& GetDominanceFrontier(
BasicBlock* bb) const;
const std::unordered_map<BasicBlock*, std::unordered_set<BasicBlock*>>&
GetAllDominanceFrontiers() const;
private:
std::unordered_map<BasicBlock*, std::unordered_set<BasicBlock*>> doms_;
std::unordered_map<BasicBlock*, BasicBlock*> idom_;
std::unordered_map<BasicBlock*, std::vector<BasicBlock*>> children_;
std::unordered_map<BasicBlock*, std::unordered_set<BasicBlock*>> df_;
};
} // namespace ir

@ -65,6 +65,56 @@ Instruction* GetTerminator(BasicBlock* bb) {
return insts.back().get();
}
// 清理 PHI 节点中对已移除前驱的引用。
// 当某个基本块不再跳转到 target_block 时target_block 的 PHI 节点
// 需要移除引用该前驱的条目。
void CleanupPhiReferences(BasicBlock* target_block, BasicBlock* removed_pred) {
auto& insts = const_cast<std::vector<std::unique_ptr<Instruction>>&>(
target_block->GetInstructions());
std::vector<PhiInst*> phis_to_delete;
for (auto& inst_ptr : insts) {
auto* phi = dynamic_cast<PhiInst*>(inst_ptr.get());
if (!phi) break;
// 收集保留的 (value, block) 对
std::vector<std::pair<Value*, BasicBlock*>> keep_pairs;
for (size_t i = 0; i < phi->GetNumOperands(); i += 2) {
auto* val = phi->GetOperand(i);
auto* pred = static_cast<BasicBlock*>(phi->GetOperand(i + 1));
if (pred != removed_pred) {
keep_pairs.push_back({val, pred});
}
}
if (keep_pairs.empty()) {
phis_to_delete.push_back(phi);
} else if (keep_pairs.size() == 1) {
phi->ReplaceAllUsesWith(keep_pairs[0].first);
phis_to_delete.push_back(phi);
} else if (keep_pairs.size() * 2 != phi->GetNumOperands()) {
// 部分条目被移除:重建操作数列表
phi->ClearOperands();
for (auto& [val, pred] : keep_pairs) {
phi->AddOperand(val);
phi->AddOperand(pred);
}
}
}
// 删除标记的 PHI 节点
if (!phis_to_delete.empty()) {
auto new_end = std::remove_if(
insts.begin(), insts.end(),
[&phis_to_delete](const std::unique_ptr<Instruction>& inst_ptr) {
return std::find(phis_to_delete.begin(), phis_to_delete.end(),
inst_ptr.get()) != phis_to_delete.end();
});
insts.erase(new_end, insts.end());
}
}
} // namespace
void RunCFGSimplify(Module& module) {
@ -93,27 +143,36 @@ void RunCFGSimplify(Module& module) {
std::vector<std::pair<PhiInst*, Value*>> phi_replacements;
std::vector<PhiInst*> phi_to_delete;
for (auto& inst_ptr : bb->GetInstructions()) {
auto* phi = dynamic_cast<PhiInst*>(inst_ptr.get());
if (!phi) break;
Value* valid_val = nullptr;
size_t valid_count = 0;
// 收集所有可达前驱对应的 (value, block) 对
std::vector<std::pair<Value*, BasicBlock*>> valid_pairs;
bool has_unreachable = false;
for (size_t i = 0; i < phi->GetNumOperands(); i += 2) {
auto* val = phi->GetOperand(i);
auto* pred = static_cast<BasicBlock*>(phi->GetOperand(i + 1));
if (unreachable.find(pred) == unreachable.end()) {
valid_val = val;
valid_count++;
valid_pairs.push_back({val, pred});
} else {
has_unreachable = true;
}
}
if (valid_count == 1 && valid_val) {
phi_replacements.push_back({phi, valid_val});
} else if (valid_count == 0) {
if (valid_pairs.size() == 1) {
phi_replacements.push_back({phi, valid_pairs[0].first});
} else if (valid_pairs.empty()) {
phi_to_delete.push_back(phi);
} else if (has_unreachable && valid_pairs.size() >= 2) {
// 部分前驱不可达:清理后仅保留可达条目
phi->ClearOperands();
for (auto& [val, pred] : valid_pairs) {
phi->AddOperand(val);
phi->AddOperand(pred);
}
}
}
@ -148,19 +207,23 @@ void RunCFGSimplify(Module& module) {
auto* bb = bb_ptr.get();
auto* term = GetTerminator(bb);
if (!term) continue;
if (auto* condbr = dynamic_cast<CondBranchInst*>(term)) {
auto* cond = condbr->GetCond();
if (auto* const_int = dynamic_cast<ConstantInt*>(cond)) {
auto* true_target = condbr->GetTrueTarget();
auto* false_target = condbr->GetFalseTarget();
auto* target = (const_int->GetValue() != 0) ? true_target : false_target;
auto* live_target = (const_int->GetValue() != 0) ? true_target : false_target;
auto* dead_target = (const_int->GetValue() != 0) ? false_target : true_target;
bb->RemoveInstruction(condbr);
bb->Append<BranchInst>(Type::GetVoidType(), target);
bb->Append<BranchInst>(Type::GetVoidType(), live_target);
changed = true;
// 清理 dead_target 的 PHI 节点中对 bb 的引用
CleanupPhiReferences(dead_target, bb);
}
}
}

@ -7,6 +7,7 @@ add_library(ir_passes STATIC
CSE.cpp
DCE.cpp
CFGSimplify.cpp
IRVerifier.cpp
)
target_link_libraries(ir_passes PUBLIC

@ -0,0 +1,214 @@
// IR 验证器:校验 IR 模块的合法性。
// 检查项:
// 1. SSA 支配性:每条指令的操作数(也是指令)必须由当前基本块支配
// 2. 基本块终结指令:每个非空基本块必须以终结指令结尾
// 3. PHI 一致性PHI 节点操作数结构正确
//
// 验证失败时输出错误信息并 abort(),仅在 Debug 模式下生效。
#include "ir/IR.h"
#include "ir/analysis/DominatorTree.h"
#include "utils/Log.h"
#include <cstdlib>
#include <iostream>
#include <sstream>
#include <unordered_map>
#include <unordered_set>
namespace ir {
namespace {
// 收集某个基本块的前驱(基于终结指令的跳转目标)
std::unordered_map<BasicBlock*, std::vector<BasicBlock*>> CollectPredecessors(
Function* func) {
std::unordered_map<BasicBlock*, std::vector<BasicBlock*>> preds;
for (const auto& bb : func->GetBlocks()) {
preds[bb.get()] = {};
}
for (const auto& bb : func->GetBlocks()) {
if (!bb->HasTerminator()) {
continue;
}
auto* term = bb->GetInstructions().back().get();
if (auto* br = dynamic_cast<BranchInst*>(term)) {
preds[br->GetTarget()].push_back(bb.get());
} else if (auto* condbr = dynamic_cast<CondBranchInst*>(term)) {
preds[condbr->GetTrueTarget()].push_back(bb.get());
preds[condbr->GetFalseTarget()].push_back(bb.get());
}
}
return preds;
}
// 验证单个函数的 IR
void VerifyFunction(Function* func) {
const auto& blocks = func->GetBlocks();
if (blocks.empty()) {
return; // 空函数external declaration无需验证
}
// 构建前驱映射(用于 PHI 验证)
auto pred_map = CollectPredecessors(func);
// 构建支配树
DominatorTree dom_tree;
dom_tree.Compute(func);
// 收集所有基本块指针用于查找
std::unordered_set<BasicBlock*> block_set;
for (const auto& bb : blocks) {
block_set.insert(bb.get());
}
for (const auto& bb : blocks) {
BasicBlock* current_bb = bb.get();
// 检查 1: 非空基本块必须以终结指令结尾
const auto& instructions = current_bb->GetInstructions();
if (!instructions.empty()) {
auto* last_inst = instructions.back().get();
if (!last_inst->IsTerminator()) {
std::ostringstream oss;
oss << "IR 验证失败: 函数 '" << func->GetName() << "' 的基本块 '"
<< current_bb->GetName()
<< "' 的最后一条指令不是终结指令 (opcode="
<< static_cast<int>(last_inst->GetOpcode()) << ")";
LogError(oss.str(), std::cerr);
std::abort();
}
}
// 检查 2: SSA 支配性 + PHI 一致性
for (const auto& inst_ptr : instructions) {
auto* inst = inst_ptr.get();
if (inst->GetOpcode() == Opcode::Phi) {
// PHI 一致性检查:
// - 操作数个数必须为偶数
// - 每个奇数索引的操作数(基本块引用)必须是前驱
size_t num_operands = inst->GetNumOperands();
if (num_operands % 2 != 0) {
std::ostringstream oss;
oss << "IR 验证失败: 函数 '" << func->GetName()
<< "' 的基本块 '" << current_bb->GetName()
<< "' 中的 PHI 节点操作数个数为奇数 (" << num_operands << ")";
LogError(oss.str(), std::cerr);
std::abort();
}
// 收集已出现的前驱,检查重复
std::unordered_set<BasicBlock*> seen_preds;
const auto& bb_preds = pred_map[current_bb];
for (size_t i = 1; i < num_operands; i += 2) {
Value* block_op = inst->GetOperand(i);
auto* pred_bb = dynamic_cast<BasicBlock*>(block_op);
if (!pred_bb) {
std::ostringstream oss;
oss << "IR 验证失败: 函数 '" << func->GetName()
<< "' 的基本块 '" << current_bb->GetName()
<< "' 中的 PHI 节点操作数 " << i << " 不是基本块";
LogError(oss.str(), std::cerr);
std::abort();
}
// 检查该基本块是否为实际前驱
bool is_pred = false;
for (auto* p : bb_preds) {
if (p == pred_bb) {
is_pred = true;
break;
}
}
if (!is_pred) {
std::ostringstream oss;
oss << "IR 验证失败: 函数 '" << func->GetName()
<< "' 的基本块 '" << current_bb->GetName()
<< "' 中的 PHI 节点引用了非前驱基本块 '"
<< pred_bb->GetName() << "'";
LogError(oss.str(), std::cerr);
std::abort();
}
// 检查重复前驱
if (seen_preds.find(pred_bb) != seen_preds.end()) {
std::ostringstream oss;
oss << "IR 验证失败: 函数 '" << func->GetName()
<< "' 的基本块 '" << current_bb->GetName()
<< "' 中的 PHI 节点包含重复前驱 '"
<< pred_bb->GetName() << "'";
LogError(oss.str(), std::cerr);
std::abort();
}
seen_preds.insert(pred_bb);
// PHI 的 SSA 支配性检查:
// 每个值操作数(偶数索引)的定义必须支配对应的前驱基本块
Value* val_op = inst->GetOperand(i - 1);
auto* val_inst = dynamic_cast<Instruction*>(val_op);
if (val_inst && val_inst->GetParent()) {
if (!dom_tree.Dominates(val_inst->GetParent(), pred_bb)) {
std::ostringstream oss;
oss << "IR 验证失败: 函数 '" << func->GetName()
<< "' 的基本块 '" << current_bb->GetName()
<< "' 中的 PHI 值操作数 '" << val_inst->GetName()
<< "' (定义于 '" << val_inst->GetParent()->GetName()
<< "') 不支配前驱 '" << pred_bb->GetName() << "'";
LogError(oss.str(), std::cerr);
std::abort();
}
}
}
} else {
// 非 PHI 指令:检查每个操作数的 SSA 支配性
for (size_t i = 0; i < inst->GetNumOperands(); ++i) {
Value* op = inst->GetOperand(i);
// 跳过常量、参数和基本块引用
if (op->IsConstant()) continue;
auto* op_bb_ref = dynamic_cast<BasicBlock*>(op);
if (op_bb_ref) continue;
auto* op_arg = dynamic_cast<Argument*>(op);
if (op_arg) continue;
if (op->IsFunction()) continue;
auto* op_inst = dynamic_cast<Instruction*>(op);
if (!op_inst) continue;
BasicBlock* def_bb = op_inst->GetParent();
if (!def_bb) continue;
// 支配性检查:定义的基本块必须支配当前基本块
if (!dom_tree.Dominates(def_bb, current_bb)) {
std::ostringstream oss;
oss << "IR 验证失败: 函数 '" << func->GetName()
<< "' 中的指令 '" << inst->GetName()
<< "' (opcode=" << static_cast<int>(inst->GetOpcode())
<< ", 基本块 '" << current_bb->GetName()
<< "') 使用了未支配的操作数 '" << op_inst->GetName()
<< "' (定义于 '" << def_bb->GetName() << "')";
LogError(oss.str(), std::cerr);
std::abort();
}
}
}
}
}
}
} // namespace
void VerifyIR(Module& module) {
for (const auto& func_ptr : module.GetFunctions()) {
auto* func = func_ptr.get();
// 跳过外部声明(没有函数体)
if (func->IsExternal()) continue;
// 跳过空函数体
if (func->GetBlocks().empty()) continue;
VerifyFunction(func);
}
}
} // namespace ir

@ -48,6 +48,11 @@ int main(int argc, char** argv) {
pass_manager.Run();
}
// Debug 模式:验证 IR 合法性
#ifndef NDEBUG
ir::VerifyIR(*module);
#endif
// 汇编输出到文件或标准输出
if (opts.emit_asm) {
auto machine_module = mir::LowerModuleToMIR(*module);

Loading…
Cancel
Save