[backend] fixed the bug of physical register allocation error
This commit is contained in:
@ -6,7 +6,7 @@
|
||||
#include <iomanip>
|
||||
#include <functional> // For std::function
|
||||
|
||||
#define DEBUG 0
|
||||
#define DEBUG 1 // 开启DEBUG以便查看新输出
|
||||
namespace sysy {
|
||||
|
||||
// 可用于分配的通用寄存器
|
||||
@ -109,6 +109,7 @@ std::string RISCv32CodeGen::function_gen(Function* func) {
|
||||
ss << func->getName() << ":\n"; // 函数入口标签
|
||||
|
||||
// 执行寄存器分配
|
||||
// 注意:vreg_counter 和 value_vreg_map 在 register_allocation 内部会被初始化
|
||||
auto alloc = register_allocation(func);
|
||||
int stack_size = alloc.stack_size;
|
||||
|
||||
@ -162,11 +163,10 @@ std::string RISCv32CodeGen::basicBlock_gen(BasicBlock* bb, const RegAllocResult&
|
||||
}
|
||||
ss << bb_name << ":\n"; // 基本块标签
|
||||
|
||||
// 为每个基本块重置虚拟寄存器映射和计数器,因为 DAG 是按块构建的
|
||||
// 注意:这里的重置可能会影响跨块的活跃性分析,如果 DAG 构建是跨块的,则不能在这里重置
|
||||
// 但目前 DAG 是按块构建,所以在此重置是合理的。
|
||||
value_vreg_map.clear();
|
||||
vreg_counter = 0; // 为每个块重置虚拟寄存器计数器
|
||||
// !!! 重要的修改:此处不再清除 value_vreg_map 和 vreg_counter。
|
||||
// !!! 这些映射在 function_gen -> register_allocation 阶段为整个函数建立。
|
||||
// value_vreg_map.clear(); // 移除此行
|
||||
// vreg_counter = 0; // 移除此行
|
||||
|
||||
// 构建当前基本块的 DAG
|
||||
// 注意:DAGNode 的唯一性在当前函数范围内是重要的,
|
||||
@ -266,22 +266,21 @@ std::vector<std::unique_ptr<RISCv32CodeGen::DAGNode>> RISCv32CodeGen::build_dag(
|
||||
// 为产生结果的值分配虚拟寄存器
|
||||
// 注意:这里的vreg分配是在每个块中独立进行的,但寄存器分配器是在函数级别运行的
|
||||
// 我们在寄存器分配前,已经为整个函数的所有value预分配了vreg
|
||||
if (val && value_vreg_map.count(val)) {
|
||||
node->result_vreg = value_vreg_map.at(val);
|
||||
} else if (val && kind != DAGNode::STORE && kind != DAGNode::RETURN && kind != DAGNode::BRANCH) {
|
||||
// 如果一个值(例如常量)在预分配阶段没有vreg,这里可以给一个
|
||||
if(value_vreg_map.find(val) == value_vreg_map.end()){
|
||||
value_vreg_map[val] = "v" + std::to_string(vreg_counter++);
|
||||
}
|
||||
// 此处的逻辑应完全依赖于 register_allocation 阶段已经建立的 value_vreg_map
|
||||
// 并且 AllocaInst 不应在此处获取 result_vreg,因为它不映射到物理寄存器。
|
||||
if (val && value_vreg_map.count(val) && !dynamic_cast<AllocaInst*>(val)) { // 排除 AllocaInst
|
||||
node->result_vreg = value_vreg_map.at(val);
|
||||
}
|
||||
|
||||
// 如果 val 即使在 value_vreg_map 中存在,但它是 AllocaInst,则不分配 result_vreg。
|
||||
// 对于 DAGNode::ALLOCA_ADDR 节点,它的 result_vreg 应该为空,因为其“值”是内存地址,
|
||||
// 在指令选择时直接转换为 s0 + 偏移量。
|
||||
|
||||
DAGNode* raw_node_ptr = node.get();
|
||||
nodes_storage.push_back(std::move(node)); // 存储 unique_ptr
|
||||
|
||||
// 仅当 IR Value 表示一个计算值时,才将其映射到创建的 DAGNode
|
||||
if (val && kind != DAGNode::STORE && kind != DAGNode::RETURN && kind != DAGNode::BRANCH) {
|
||||
// 且它应该已经在 register_allocation 中被分配了 vreg
|
||||
if (val && value_vreg_map.count(val) && kind != DAGNode::STORE && kind != DAGNode::RETURN && kind != DAGNode::BRANCH && !dynamic_cast<AllocaInst*>(val)) {
|
||||
value_to_node[val] = raw_node_ptr;
|
||||
}
|
||||
return raw_node_ptr;
|
||||
@ -296,9 +295,9 @@ std::vector<std::unique_ptr<RISCv32CodeGen::DAGNode>> RISCv32CodeGen::build_dag(
|
||||
// 创建一个节点来表示分配内存的地址。
|
||||
// 这个地址将是 s0 (帧指针) 的偏移量。
|
||||
// 我们将 AllocaInst 指针存储在 DAGNode 的 `value` 字段中。
|
||||
auto alloca_addr_node = create_node(DAGNode::ALLOCA_ADDR, alloca);
|
||||
// 此节点将有一个 result_vreg,表示计算出的地址 (s0 + 偏移量)
|
||||
// 实际偏移量将在寄存器分配阶段 (stack_map) 确定。
|
||||
// 修正:AllocaInst 类型的 DAGNode 应该有一个 value 对应 AllocaInst*
|
||||
// 但它本身不应该有 result_vreg,因为不映射到物理寄存器。
|
||||
create_node(DAGNode::ALLOCA_ADDR, alloca);
|
||||
} else if (auto store = dynamic_cast<StoreInst*>(inst)) {
|
||||
auto store_node = create_node(DAGNode::STORE, store); // 将 store inst 绑定到 node
|
||||
|
||||
@ -321,6 +320,7 @@ std::vector<std::unique_ptr<RISCv32CodeGen::DAGNode>> RISCv32CodeGen::build_dag(
|
||||
} else if (auto alloca = dynamic_cast<AllocaInst*>(ptr_ir)) {
|
||||
// 如果是alloca,我们应该找到代表它地址的节点
|
||||
// 为了简化,如果没找到,就创建一个
|
||||
// 修正:AllocaInst 应该直接映射到 ALLOCA_ADDR 节点,其值是 AllocaInst*
|
||||
ptr_node = create_node(DAGNode::ALLOCA_ADDR, alloca);
|
||||
} else if (auto global = dynamic_cast<GlobalValue*>(ptr_ir)) {
|
||||
ptr_node = create_node(DAGNode::CONSTANT, global); // 全局地址将被加载
|
||||
@ -342,6 +342,7 @@ std::vector<std::unique_ptr<RISCv32CodeGen::DAGNode>> RISCv32CodeGen::build_dag(
|
||||
if (value_to_node.count(ptr_ir)) {
|
||||
ptr_node = value_to_node[ptr_ir];
|
||||
} else if (auto alloca = dynamic_cast<AllocaInst*>(ptr_ir)) {
|
||||
// 修正:AllocaInst 应该直接映射到 ALLOCA_ADDR 节点
|
||||
ptr_node = create_node(DAGNode::ALLOCA_ADDR, alloca);
|
||||
} else if (auto global = dynamic_cast<GlobalValue*>(ptr_ir)) {
|
||||
ptr_node = create_node(DAGNode::CONSTANT, global); // 全局地址将被加载
|
||||
@ -436,7 +437,7 @@ std::vector<std::unique_ptr<RISCv32CodeGen::DAGNode>> RISCv32CodeGen::build_dag(
|
||||
return nodes_storage;
|
||||
}
|
||||
|
||||
// 打印 DAG
|
||||
// 打印 DAG (保持不变)
|
||||
void RISCv32CodeGen::print_dag(const std::vector<std::unique_ptr<DAGNode>>& dag, const std::string& bb_name) {
|
||||
std::cerr << "=== DAG for Basic Block: " << bb_name << " ===\n";
|
||||
std::set<DAGNode*> visited;
|
||||
@ -514,7 +515,7 @@ void RISCv32CodeGen::print_dag(const std::vector<std::unique_ptr<DAGNode>>& dag,
|
||||
}
|
||||
|
||||
|
||||
// 指令选择
|
||||
// 指令选择 (保持不变,因为问题不在选择器本身,而是分配)
|
||||
void RISCv32CodeGen::select_instructions(DAGNode* node, const RegAllocResult& alloc) {
|
||||
if (!node) return;
|
||||
if (!node->inst.empty()) return; // 指令已选择
|
||||
@ -529,14 +530,18 @@ void RISCv32CodeGen::select_instructions(DAGNode* node, const RegAllocResult& al
|
||||
std::stringstream ss_inst; // 使用 stringstream 构建指令
|
||||
|
||||
// 获取分配的物理寄存器,如果没有分配,则使用临时寄存器 (T0)
|
||||
// 这是关键:这里应该返回寄存器分配器实际分配的寄存器。
|
||||
// 如果寄存器分配失败,才会回退到 T0。
|
||||
auto get_preg_or_temp = [&](const std::string& vreg) {
|
||||
if (alloc.vreg_to_preg.count(vreg)) {
|
||||
return reg_to_string(alloc.vreg_to_preg.at(vreg));
|
||||
}
|
||||
// 如果虚拟寄存器未分配给物理寄存器,则表示它已溢出或是一个临时值。
|
||||
// 为简化,我们使用固定临时寄存器 t0 用于溢出值或需要加载到寄存器中的立即数。
|
||||
// 更健壮的后端会在此处显式处理溢出。
|
||||
return reg_to_string(PhysicalReg::T0); // 回退到临时寄存器
|
||||
// 对于这个简化示例,我们没有实现完整的溢出。
|
||||
// 如果它没有被分配(例如,因为它没有被认为是活跃的,或者寄存器不够),
|
||||
// 那么我们将使用 T0 作为回退。这可能是导致问题的根本原因。
|
||||
// 但对于简单程序,如果活跃性分析正确,它应该能被分配。
|
||||
return reg_to_string(PhysicalReg::T0); // 回退到临时寄存器 t0
|
||||
};
|
||||
|
||||
// 获取分配的栈变量的内存偏移量
|
||||
@ -598,12 +603,9 @@ void RISCv32CodeGen::select_instructions(DAGNode* node, const RegAllocResult& al
|
||||
std::string src_reg;
|
||||
if (val_node->kind == DAGNode::CONSTANT) {
|
||||
// 如果存储的是常量,先将其加载到临时寄存器 (t0)
|
||||
if (auto constant = dynamic_cast<ConstantValue*>(val_node->value)) {
|
||||
src_reg = get_preg_or_temp(val_node->result_vreg); // 常量也应该有vreg
|
||||
// 注意:这里的li指令会由CONSTANT节点自己生成,STORE节点不应重复生成
|
||||
} else { // 存储全局地址
|
||||
src_reg = get_preg_or_temp(val_node->result_vreg);
|
||||
}
|
||||
// 这里的处理是,常量会先被其 CONSTANT 节点选择指令并存入其 result_vreg 对应的物理寄存器。
|
||||
// STORE 节点直接使用这个物理寄存器。
|
||||
src_reg = get_preg_or_temp(val_node->result_vreg);
|
||||
} else {
|
||||
src_reg = get_preg_or_temp(val_node->result_vreg);
|
||||
}
|
||||
@ -820,8 +822,23 @@ void RISCv32CodeGen::emit_instructions(DAGNode* node, std::stringstream& ss, con
|
||||
}
|
||||
}
|
||||
|
||||
// 辅助函数:将集合打印为字符串
|
||||
std::string print_set(const std::set<std::string>& s) {
|
||||
std::stringstream ss;
|
||||
ss << "{";
|
||||
bool first = true;
|
||||
for (const auto& elem : s) {
|
||||
if (!first) {
|
||||
ss << ", ";
|
||||
}
|
||||
ss << elem;
|
||||
first = false;
|
||||
}
|
||||
ss << "}";
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
// 活跃性分析 (基本保持不变,因为是通用的数据流分析)
|
||||
// 活跃性分析
|
||||
std::map<Instruction*, std::set<std::string>> RISCv32CodeGen::liveness_analysis(Function* func) {
|
||||
std::map<Instruction*, std::set<std::string>> live_in, live_out;
|
||||
bool changed = true;
|
||||
@ -834,69 +851,141 @@ std::map<Instruction*, std::set<std::string>> RISCv32CodeGen::liveness_analysis(
|
||||
}
|
||||
}
|
||||
|
||||
int iteration_count = 0; // 迭代计数器
|
||||
while (changed) {
|
||||
changed = false;
|
||||
iteration_count++;
|
||||
std::cout << "\n--- 活跃性分析迭代: " << iteration_count << " ---" << std::endl;
|
||||
|
||||
// 逆序遍历基本块
|
||||
for (auto it = func->getBasicBlocks_NoRange().rbegin(); it != func->getBasicBlocks_NoRange().rend(); ++it) {
|
||||
auto bb = it->get();
|
||||
std::cout << " 基本块: " << bb->getName() << std::endl; // 打印基本块名称
|
||||
|
||||
// 在基本块内逆序遍历指令
|
||||
// live_out_for_bb_inst 是当前基本块的 live_out 集合,用于计算该基本块中第一条指令的 live_out
|
||||
// 初始化为当前基本块所有后继基本块的 live_in 的并集
|
||||
std::set<std::string> live_out_for_bb_inst = {};
|
||||
for (const auto& succ_bb : bb->getSuccessors()) {
|
||||
if (!succ_bb->getInstructions().empty()) {
|
||||
Instruction* first_inst_in_succ = succ_bb->getInstructions().front().get();
|
||||
live_out_for_bb_inst.insert(live_in[first_inst_in_succ].begin(), live_in[first_inst_in_succ].end());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
for (auto inst_it = bb->getInstructions().rbegin(); inst_it != bb->getInstructions().rend(); ++inst_it) {
|
||||
auto inst = inst_it->get();
|
||||
std::set<std::string> current_live_in = live_in[inst];
|
||||
std::set<std::string> current_live_out = live_out[inst];
|
||||
// 打印指令,使用其父基本块的名称和指令地址作为唯一标识符
|
||||
std::cout << " 指令 (BB: " << bb->getName() << ", 地址: " << static_cast<void*>(inst) << ")" << std::endl;
|
||||
|
||||
std::set<std::string> new_live_out;
|
||||
// 后继的 live_in 集合的并集
|
||||
if (inst->isTerminator()) {
|
||||
if (auto br = dynamic_cast<CondBrInst*>(inst)) {
|
||||
new_live_out.insert(live_in[br->getThenBlock()->getInstructions().front().get()].begin(),
|
||||
live_in[br->getThenBlock()->getInstructions().front().get()].end());
|
||||
new_live_out.insert(live_in[br->getElseBlock()->getInstructions().front().get()].begin(),
|
||||
live_in[br->getElseBlock()->getInstructions().front().get()].end());
|
||||
} else if (auto uncond = dynamic_cast<UncondBrInst*>(inst)) {
|
||||
new_live_out.insert(live_in[uncond->getBlock()->getInstructions().front().get()].begin(),
|
||||
live_in[uncond->getBlock()->getInstructions().front().get()].end());
|
||||
}
|
||||
// 返回指令没有后继,所以 new_live_out 保持为空
|
||||
std::set<std::string> current_live_in = live_in[inst];
|
||||
std::set<std::string> current_live_out = live_out[inst]; // 用于比较的旧 live_out
|
||||
|
||||
std::set<std::string> new_live_out_calc; // 用于计算的 live_out
|
||||
|
||||
// 如果当前指令是基本块中的最后一条指令 (即逆序遍历中的第一条指令)
|
||||
// 那么它的 live_out 就是该基本块的 live_out,即其所有后继基本块的 live_in 的并集
|
||||
if (inst_it == bb->getInstructions().rbegin()) {
|
||||
new_live_out_calc = live_out_for_bb_inst;
|
||||
std::cout << " 指令是基本块的最后一条指令,live_out 取自后继基本块 live_in 的并集: " << print_set(new_live_out_calc) << std::endl;
|
||||
} else {
|
||||
auto next_inst_it = std::next(inst_it);
|
||||
if (next_inst_it != bb->getInstructions().rend()) {
|
||||
// 不是终结符,所以块中的下一条指令是后继
|
||||
new_live_out = live_in[next_inst_it->get()];
|
||||
}
|
||||
// 如果不是基本块的最后一条指令,则其 live_out 是其后继指令的 live_in
|
||||
auto prev_inst_it = std::prev(inst_it); // std::prev 获取正向的下一条指令
|
||||
new_live_out_calc = live_in[prev_inst_it->get()];
|
||||
std::cout << " 指令不是基本块的最后一条,其 live_out 是其后继指令 live_in: " << print_set(new_live_out_calc) << std::endl;
|
||||
}
|
||||
|
||||
|
||||
// 计算当前指令的 use 和 def 集合
|
||||
std::set<std::string> use_set, def_set;
|
||||
|
||||
// 定义 (Def)
|
||||
if (value_vreg_map.count(inst)) {
|
||||
// 定义 (Def) - 只有当指令本身产生一个非 void 结果并映射到虚拟寄存器时
|
||||
// LoadInst, BinaryInst, CallInst 等会定义一个结果。
|
||||
// StoreInst, AllocaInst, ReturnInst, BranchInst 不定义结果到寄存器。
|
||||
if (!inst->getType()->isVoid() && !dynamic_cast<AllocaInst*>(inst) && !dynamic_cast<StoreInst*>(inst) &&
|
||||
!dynamic_cast<ReturnInst*>(inst) && !dynamic_cast<CondBrInst*>(inst) && !dynamic_cast<UncondBrInst*>(inst) && value_vreg_map.count(inst)) {
|
||||
def_set.insert(value_vreg_map.at(inst));
|
||||
std::cout << " 指令 (地址: " << static_cast<void*>(inst) << ") 定义了虚拟寄存器: " << value_vreg_map.at(inst) << std::endl;
|
||||
}
|
||||
|
||||
// *** 针对 StoreInst 的新逻辑来“杀死”被存储值的虚拟寄存器 ***
|
||||
// 这个逻辑在活跃性分析中需要非常谨慎。通常,Store 指令是使用(use)被存储的值,而不是定义(def)它。
|
||||
// 如果一个值在 Store 后不再被使用,它会自然地通过活跃性传播变得不活跃。
|
||||
// 将被存储的值添加到 def_set 意味着该值在该指令处被“杀死”,因为它被存储到内存中,而不是寄存器。
|
||||
// 对于“01_add.sy”这样的简单情况,这可能是有效的,但对于更复杂的程序,可能会导致不准确的活跃性。
|
||||
// 考虑一个值被存储,但稍后又从内存中加载并再次使用的情况。
|
||||
// 这是一个需要仔细考虑的启发式方法。
|
||||
if (auto store = dynamic_cast<StoreInst*>(inst)) {
|
||||
Value* stored_value = store->getValue();
|
||||
// 如果被存储的值有一个虚拟寄存器,并且它不是 AllocaInst(Alloca是地址,不是寄存器值)
|
||||
if (value_vreg_map.count(stored_value) && !dynamic_cast<AllocaInst*>(stored_value)) {
|
||||
// 启发式:如果存储的值是常量或临时指令结果,并且仅在此处使用,则可以认为是“杀死”了该虚拟寄存器。
|
||||
// 更准确的判断需要检查该值的其他所有用途。
|
||||
// 对于 '01_add.sy' 中常量的情况,这是有效的。
|
||||
// 修正:使用 ->get() 来获取 shared_ptr 持有的原始指针,然后调用 getUser()
|
||||
// 检查 inst 是否是 stored_value 的唯一用户。
|
||||
bool is_unique_user = true;
|
||||
if (!stored_value->getUses().empty()) {
|
||||
is_unique_user = (stored_value->getUses().size() == 1 && stored_value->getUses().front()->getUser() == inst);
|
||||
} else {
|
||||
// 如果没有uses,则它没有被使用,不应该被def。
|
||||
is_unique_user = false;
|
||||
}
|
||||
|
||||
// 使用 (Use) - 遍历指令的操作数
|
||||
for(const auto& operand_use : inst->getOperands()){
|
||||
Value* operand = operand_use->getValue();
|
||||
// 只有非立即数的值才生活在虚拟寄存器中
|
||||
if(!dynamic_cast<ConstantValue*>(operand) && value_vreg_map.count(operand)){
|
||||
use_set.insert(value_vreg_map.at(operand));
|
||||
if (is_unique_user) {
|
||||
def_set.insert(value_vreg_map.at(stored_value));
|
||||
std::cout << " Store 指令 (地址: " << static_cast<void*>(inst) << ") 将被存储的值 '" << value_vreg_map.at(stored_value) << "' 添加到 def_set (启发式)." << std::endl;
|
||||
} else {
|
||||
std::cout << " Store 指令 (地址: " << static_cast<void*>(inst) << ") 存储的值 '" << value_vreg_map.at(stored_value) << "' 有其他用途或不是唯一用途,未添加到 def_set。" << std::endl;
|
||||
}
|
||||
} else if (dynamic_cast<AllocaInst*>(stored_value)) {
|
||||
std::cout << " Store 指令存储的是 AllocaInst 地址,不处理其虚拟寄存器定义。" << std::endl;
|
||||
}
|
||||
}
|
||||
// *** 结束新逻辑 ***
|
||||
|
||||
// 使用 (Use) - 遍历指令的操作数
|
||||
for(const auto& operand_use : inst->getOperands()){
|
||||
Value* operand = operand_use->getValue();
|
||||
// 只有当操作数是一个实际需要寄存器来存储的“值”时,才将其vreg添加到use_set。
|
||||
// 排除 AllocaInst,因为它们是地址概念,不占用通用寄存器。
|
||||
// 并且确保 operand 已经在 value_vreg_map 中有对应的虚拟寄存器。
|
||||
if (value_vreg_map.count(operand) && !dynamic_cast<AllocaInst*>(operand)) {
|
||||
use_set.insert(value_vreg_map.at(operand));
|
||||
std::cout << " 指令 (地址: " << static_cast<void*>(inst) << ") 使用了虚拟寄存器: " << value_vreg_map.at(operand) << std::endl;
|
||||
} else if (dynamic_cast<AllocaInst*>(operand)) {
|
||||
std::cout << " 指令 (地址: " << static_cast<void*>(inst) << ") 操作数是 AllocaInst 地址,不添加到 use_set。" << std::endl;
|
||||
} else {
|
||||
// 对于常量,它们没有虚拟寄存器,也不应该被添加到use_set
|
||||
// 也可以是其他没有对应虚拟寄存器(例如函数名)的值。
|
||||
std::cout << " 指令 (地址: " << static_cast<void*>(inst) << ") 操作数没有对应的虚拟寄存器,或不是需要寄存器的值。" << std::endl;
|
||||
}
|
||||
}
|
||||
std::cout << " 指令 (地址: " << static_cast<void*>(inst) << ") 的 use_set: " << print_set(use_set) << std::endl;
|
||||
std::cout << " 指令 (地址: " << static_cast<void*>(inst) << ") 的 def_set: " << print_set(def_set) << std::endl;
|
||||
|
||||
|
||||
// 计算新的 live_in = use U (new_live_out - def)
|
||||
// 计算新的 live_in = use U (new_live_out_calc - def)
|
||||
std::set<std::string> new_live_in = use_set;
|
||||
for (const auto& vreg : new_live_out) {
|
||||
if (def_set.find(vreg) == def_set.end()) {
|
||||
for (const auto& vreg : new_live_out_calc) {
|
||||
if (def_set.find(vreg) == def_set.end()) { // 如果 vreg 在 new_live_out_calc 中但不在 def 中,则它活跃
|
||||
new_live_in.insert(vreg);
|
||||
}
|
||||
}
|
||||
|
||||
std::cout << " 指令 (地址: " << static_cast<void*>(inst) << ") 计算出的 new_live_in: " << print_set(new_live_in) << std::endl;
|
||||
std::cout << " 指令 (地址: " << static_cast<void*>(inst) << ") 当前 live_in: " << print_set(current_live_in) << ", 当前 live_out: " << print_set(current_live_out) << std::endl;
|
||||
|
||||
|
||||
// 检查收敛
|
||||
if (new_live_in != current_live_in || new_live_out != current_live_out) {
|
||||
if (new_live_in != current_live_in || new_live_out_calc != current_live_out) { // 注意这里要用 new_live_out_calc 比较
|
||||
live_in[inst] = new_live_in;
|
||||
live_out[inst] = new_live_out;
|
||||
live_out[inst] = new_live_out_calc; // 更新 live_out
|
||||
changed = true;
|
||||
std::cout << " 指令 (地址: " << static_cast<void*>(inst) << ") 活跃性集合发生变化,更新并继续迭代." << std::endl;
|
||||
} else {
|
||||
std::cout << " 指令 (地址: " << static_cast<void*>(inst) << ") 活跃性集合未发生变化." << std::endl;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -921,7 +1010,9 @@ std::map<std::string, std::set<std::string>> RISCv32CodeGen::build_interference_
|
||||
const auto& live_after_inst = pair.second; // 这实际上是下一条指令/基本块入口的 live_in
|
||||
|
||||
std::string defined_vreg;
|
||||
if (value_vreg_map.count(inst)) {
|
||||
// 修正:只有当指令结果是需要物理寄存器时才视为定义。
|
||||
// AllocaInst 不应在此处处理。
|
||||
if (value_vreg_map.count(inst) && !dynamic_cast<AllocaInst*>(inst)) {
|
||||
defined_vreg = value_vreg_map.at(inst);
|
||||
}
|
||||
|
||||
@ -958,7 +1049,7 @@ void RISCv32CodeGen::color_graph(std::map<std::string, PhysicalReg>& vreg_to_pre
|
||||
// 收集干扰邻居的颜色
|
||||
if (interference_graph.count(vreg)) {
|
||||
for (const auto& neighbor_vreg : interference_graph.at(vreg)) {
|
||||
if (vreg_to_preg.count(neighbor_vreg)) {
|
||||
if (vreg_to_preg.count(neighbor_vreg)) { // 只有当邻居已被着色时才考虑
|
||||
used_colors.insert(vreg_to_preg.at(neighbor_vreg));
|
||||
}
|
||||
}
|
||||
@ -990,7 +1081,7 @@ RISCv32CodeGen::RegAllocResult RISCv32CodeGen::register_allocation(Function* fun
|
||||
// 1. Phi 节点消除 (如果 IR 中有 Phi 节点,需要在活跃性分析前消除)
|
||||
eliminate_phi(func); // 确保首先调用此函数
|
||||
|
||||
// 为每个函数重置计数器
|
||||
// 为每个函数重置计数器和虚拟寄存器映射
|
||||
vreg_counter = 0;
|
||||
value_vreg_map.clear(); // 为每个函数清除
|
||||
|
||||
@ -1002,17 +1093,29 @@ RISCv32CodeGen::RegAllocResult RISCv32CodeGen::register_allocation(Function* fun
|
||||
// 如果指令产生一个非 void 的结果,它就需要一个地方来存储这个结果。
|
||||
// 我们为其分配一个虚拟寄存器。
|
||||
if (!inst->getType()->isVoid()) {
|
||||
if (value_vreg_map.find(inst) == value_vreg_map.end()) {
|
||||
value_vreg_map[inst] = "v" + std::to_string(vreg_counter++);
|
||||
// 修正:确保 AllocaInst 不在这里分配vreg,因为它不占用物理寄存器
|
||||
if (!dynamic_cast<AllocaInst*>(inst)) { // 排除 AllocaInst
|
||||
if (value_vreg_map.find(inst) == value_vreg_map.end()) {
|
||||
value_vreg_map[inst] = "v" + std::to_string(vreg_counter++);
|
||||
}
|
||||
}
|
||||
}
|
||||
// 也为常量操作数分配vreg,以便它们可以参与活跃性分析
|
||||
// 也为常量操作数和全局变量操作数分配vreg,以便它们可以参与活跃性分析
|
||||
for(const auto& operand_use : inst->getOperands()){
|
||||
Value* operand = operand_use->getValue();
|
||||
// 修正:同样排除 AllocaInst 作为操作数
|
||||
if(dynamic_cast<ConstantValue*>(operand) || dynamic_cast<GlobalValue*>(operand)){
|
||||
if (value_vreg_map.find(operand) == value_vreg_map.end()) {
|
||||
value_vreg_map[operand] = "v" + std::to_string(vreg_counter++);
|
||||
}
|
||||
} else if (dynamic_cast<Instruction*>(operand) && dynamic_cast<Instruction*>(operand)->getType()->isVoid() == false) {
|
||||
// 如果操作数是另一个指令的结果且非void,确保它也有vreg
|
||||
// 修正:再次排除 AllocaInst
|
||||
if (!dynamic_cast<AllocaInst*>(operand)) {
|
||||
if (value_vreg_map.find(operand) == value_vreg_map.end()) {
|
||||
value_vreg_map[operand] = "v" + std::to_string(vreg_counter++);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1058,6 +1161,54 @@ RISCv32CodeGen::RegAllocResult RISCv32CodeGen::register_allocation(Function* fun
|
||||
// 完整的溢出处理逻辑比较复杂,这里暂时省略。
|
||||
// 如果一个vreg没有被着色,get_preg_or_temp会回退到t0,这对于简单情况可能够用。
|
||||
|
||||
// 打印寄存器分配结果 (调试用)
|
||||
std::cerr << "=== 寄存器分配结果 (vreg_to_preg) ===\n";
|
||||
for (const auto& pair : alloc_result.vreg_to_preg) {
|
||||
std::cerr << " " << pair.first << " -> " << reg_to_string(pair.second) << "\n";
|
||||
}
|
||||
std::cerr << "=== 寄存器分配结果结束 ===\n\n";
|
||||
|
||||
// 打印活跃性分析结果 (调试用)
|
||||
std::cerr << "=== 活跃性分析结果 (live_in sets) ===\n";
|
||||
for (const auto& bb_ptr : func->getBasicBlocks()) {
|
||||
std::cerr << "Basic Block: " << bb_ptr->getName() << "\n";
|
||||
for (const auto& inst_ptr : bb_ptr->getInstructions()) {
|
||||
std::cerr << " Inst: " << inst_ptr->getKindString();
|
||||
if (!inst_ptr->getName().empty()) {
|
||||
std::cerr << "(" << inst_ptr->getName() << ")";
|
||||
}
|
||||
if (value_vreg_map.count(inst_ptr.get())) {
|
||||
std::cerr << " (Def vreg: " << value_vreg_map.at(inst_ptr.get()) << ")";
|
||||
}
|
||||
std::cerr << " (Live In: {";
|
||||
bool first = true;
|
||||
if (live_sets.count(inst_ptr.get())) {
|
||||
for (const auto& vreg : live_sets.at(inst_ptr.get())) {
|
||||
if (!first) std::cerr << ", ";
|
||||
std::cerr << vreg;
|
||||
first = false;
|
||||
}
|
||||
}
|
||||
std::cerr << "})\n";
|
||||
}
|
||||
}
|
||||
std::cerr << "=== 活跃性分析结果结束 ===\n\n";
|
||||
|
||||
// 打印干扰图 (调试用)
|
||||
std::cerr << "=== 干扰图 ===\n";
|
||||
for (const auto& pair : interference_graph) {
|
||||
std::cerr << " " << pair.first << ": {";
|
||||
bool first = true;
|
||||
for (const auto& neighbor : pair.second) {
|
||||
if (!first) std::cerr << ", ";
|
||||
std::cerr << neighbor;
|
||||
first = false;
|
||||
}
|
||||
std::cerr << "}\n";
|
||||
}
|
||||
std::cerr << "=== 干扰图结束 ===\n\n";
|
||||
|
||||
|
||||
return alloc_result;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user