[backend] fixed many bugs

This commit is contained in:
Lixuanwang
2025-06-24 03:23:45 +08:00
parent 20cc08708a
commit 395e6e4003
2 changed files with 75 additions and 112 deletions

View File

@@ -264,16 +264,18 @@ std::vector<std::unique_ptr<RISCv32CodeGen::DAGNode>> RISCv32CodeGen::build_dag(
node->value = val;
// 为产生结果的值分配虚拟寄存器
if (val && kind != DAGNode::STORE && kind != DAGNode::RETURN && kind != DAGNode::BRANCH && kind != DAGNode::ALLOCA_ADDR) {
node->result_vreg = "v" + std::to_string(vreg_counter++);
value_vreg_map[val] = node->result_vreg; // 将 IR Value 映射到其虚拟寄存器
} else if (kind == DAGNode::ALLOCA_ADDR) {
// 对于 AllocaInst 的地址,我们将虚拟寄存器分配给 DAGNode 本身,
// 而不是直接在 value_vreg_map 中分配给 AllocaInst (因为 AllocaInst 是内存位置)。
node->result_vreg = "v" + std::to_string(vreg_counter++);
// 考虑是否需要将 AllocaInst 映射到其虚拟寄存器,如果它被视为指针值
// value_vreg_map[val] = node->result_vreg; // 考虑这是否需要/正确
// 注意这里的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++);
}
node->result_vreg = value_vreg_map.at(val);
}
DAGNode* raw_node_ptr = node.get();
nodes_storage.push_back(std::move(node)); // 存储 unique_ptr
@@ -317,6 +319,8 @@ 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)) {
// 如果是alloca我们应该找到代表它地址的节点
// 为了简化,如果没找到,就创建一个
ptr_node = create_node(DAGNode::ALLOCA_ADDR, alloca);
} else if (auto global = dynamic_cast<GlobalValue*>(ptr_ir)) {
ptr_node = create_node(DAGNode::CONSTANT, global); // 全局地址将被加载
@@ -513,8 +517,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;
// ALLOCA_ADDR 节点不直接映射到一条指令,它表示的是地址的计算
if (!node->inst.empty() && node->kind != DAGNode::ALLOCA_ADDR) return; // 指令已选择
if (!node->inst.empty()) return; // 指令已选择
// 首先递归地为操作数选择指令
for (auto operand : node->operands) {
@@ -564,14 +567,9 @@ void RISCv32CodeGen::select_instructions(DAGNode* node, const RegAllocResult& al
break;
}
case DAGNode::ALLOCA_ADDR: {
// 对于 AllocaInst我们想计算其地址 (s0 + 偏移量) 并放入 result_vreg
if (auto alloca_inst = dynamic_cast<AllocaInst*>(node->value)) {
std::string dest_reg = get_preg_or_temp(node->result_vreg);
int offset = alloc.stack_map.at(alloca_inst);
// 帧指针 s0 已经指向当前帧的基地址。
// 偏移量是相对于 s0 的。
ss_inst << "addi " << dest_reg << ", s0, " << offset;
}
// FIX: 这个节点本身不生成指令。
// 它的使用者LOAD/STORE会利用它的信息生成更优化的寻址指令。
// 将 node->inst 留空以避免生成冗余的 `addi` 指令。
break;
}
case DAGNode::LOAD: {
@@ -601,18 +599,14 @@ void RISCv32CodeGen::select_instructions(DAGNode* node, const RegAllocResult& al
if (val_node->kind == DAGNode::CONSTANT) {
// 如果存储的是常量,先将其加载到临时寄存器 (t0)
if (auto constant = dynamic_cast<ConstantValue*>(val_node->value)) {
src_reg = reg_to_string(PhysicalReg::T0); // 使用临时寄存器用于常量
ss_inst << "li " << src_reg << ", " << constant->getInt(); // 这行指令将作为 store 指令的一部分被添加
src_reg = get_preg_or_temp(val_node->result_vreg); // 常量也应该有vreg
// 注意这里的li指令会由CONSTANT节点自己生成STORE节点不应重复生成
} else { // 存储全局地址
src_reg = reg_to_string(PhysicalReg::T0); // 使用临时寄存器
ss_inst << "la " << src_reg << ", " << dynamic_cast<GlobalValue*>(val_node->value)->getName();
src_reg = get_preg_or_temp(val_node->result_vreg);
}
} else {
src_reg = get_preg_or_temp(val_node->result_vreg);
}
// 将 li/la 指令与 sw 指令放在同一行,用 \n 分隔emit_instructions 会正确处理
// 这里将 store 指令放在 li/la 指令的后面
ss_inst << (ss_inst.str().empty() ? "" : "\n"); // 如果前面有指令,则换行
// 检查指针是否是 AllocaInst (栈变量)
if (ptr_node->kind == DAGNode::ALLOCA_ADDR) {
@@ -803,52 +797,20 @@ void RISCv32CodeGen::emit_instructions(DAGNode* node, std::stringstream& ss, con
// 处理虚拟寄存器替换和溢出/加载逻辑
std::string processed_line = line;
// 如果是 store 或 load并且操作数是 ALLOCA_ADDR那么地址计算addi s0, offset应该在实际的 sw/lw 之前
// 但由于 DAG 结构ALLOCA_ADDR 节点本身会生成一条 addi 指令。
// 我们需要确保这条 addi 指令在 store/load 之前被发射。
// emit_instructions 的递归调用已经处理了操作数的发射,所以 `ptr_node->inst` 应该已经生成。
// 注意:这里的替换逻辑比较脆弱,因为 select_instructions 已经直接生成了物理寄存器名
// 在一个更健壮的系统中select_instructions 会生成带vreg的指令而这里会进行替换
// 当前的实现下,这个替换逻辑大部分时间是空操作,但为了安全保留
// 替换结果虚拟寄存器 (如果此行中存在)
if (!node->result_vreg.empty()) {
std::string preg = reg_to_string(PhysicalReg::T0); // 默认到 T0
if (alloc.vreg_to_preg.count(node->result_vreg)) {
preg = reg_to_string(alloc.vreg_to_preg.at(node->result_vreg));
}
// 如果结果需要溢出到栈
if (node->value && alloc.stack_map.count(node->value) && alloc.vreg_to_preg.find(node->result_vreg) == alloc.vreg_to_preg.end()) {
// 这意味着此指令的结果将溢出。我们应该在计算后生成一个存储指令。
// 注意:这是一个简化的溢出方法;真实的溢出策略更复杂。
int offset = alloc.stack_map.at(node->value);
std::string spill_reg = reg_to_string(PhysicalReg::T0); // 使用 t0 进行溢出
// 将结果寄存器替换为 spill_reg
processed_line = std::regex_replace(processed_line, std::regex("\\b" + node->result_vreg + "\\b"), spill_reg);
// 如果当前节点不是 STORE 本身,则需要添加一个 store 指令
if (node->kind != DAGNode::STORE) {
std::string store_inst = "sw " + spill_reg + ", " + std::to_string(offset) + "(s0)";
ss << " " << store_inst << "\n";
}
} else {
// 如果结果不溢出或者已经被分配了物理寄存器
if (!node->result_vreg.empty() && alloc.vreg_to_preg.count(node->result_vreg)) {
std::string preg = reg_to_string(alloc.vreg_to_preg.at(node->result_vreg));
processed_line = std::regex_replace(processed_line, std::regex("\\b" + node->result_vreg + "\\b"), preg);
}
}
// 替换操作数虚拟寄存器 (如果此行中存在)
for (auto operand : node->operands) {
if (operand && !operand->result_vreg.empty()) {
std::string operand_preg = reg_to_string(PhysicalReg::T0);
if (alloc.vreg_to_preg.count(operand->result_vreg)) {
operand_preg = reg_to_string(alloc.vreg_to_preg.at(operand->result_vreg));
} else if (operand->value && alloc.stack_map.count(operand->value)) {
// 此操作数已溢出,在使用前将其加载到临时寄存器 (t0)。
int offset = alloc.stack_map.at(operand->value);
std::string load_inst = "lw " + reg_to_string(PhysicalReg::T0) + ", " + std::to_string(offset) + "(s0)";
// 这里直接发射 load 指令
ss << " " << load_inst << "\n";
operand_preg = reg_to_string(PhysicalReg::T0); // 使用 t0 作为此指令的源
}
if (operand && !operand->result_vreg.empty() && alloc.vreg_to_preg.count(operand->result_vreg)) {
std::string operand_preg = reg_to_string(alloc.vreg_to_preg.at(operand->result_vreg));
processed_line = std::regex_replace(processed_line, std::regex("\\b" + operand->result_vreg + "\\b"), operand_preg);
}
}
@@ -912,27 +874,15 @@ std::map<Instruction*, std::set<std::string>> RISCv32CodeGen::liveness_analysis(
def_set.insert(value_vreg_map.at(inst));
}
// 使用 (Use)
if (auto bin = dynamic_cast<BinaryInst*>(inst)) {
if (value_vreg_map.count(bin->getLhs())) use_set.insert(value_vreg_map.at(bin->getLhs()));
if (value_vreg_map.count(bin->getRhs())) use_set.insert(value_vreg_map.at(bin->getRhs()));
} else if (auto call = dynamic_cast<CallInst*>(inst)) {
for (auto arg : call->getArguments()) {
if (value_vreg_map.count(arg->getValue())) use_set.insert(value_vreg_map.at(arg->getValue()));
// 使用 (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));
}
} else if (auto load = dynamic_cast<LoadInst*>(inst)) {
if (value_vreg_map.count(load->getPointer())) use_set.insert(value_vreg_map.at(load->getPointer()));
} else if (auto store = dynamic_cast<StoreInst*>(inst)) {
if (value_vreg_map.count(store->getValue())) use_set.insert(value_vreg_map.at(store->getValue()));
if (value_vreg_map.count(store->getPointer())) use_set.insert(value_vreg_map.at(store->getPointer()));
} else if (auto ret = dynamic_cast<ReturnInst*>(inst)) {
if (ret->hasReturnValue() && value_vreg_map.count(ret->getReturnValue()))
use_set.insert(value_vreg_map.at(ret->getReturnValue()));
} else if (auto cond_br = dynamic_cast<CondBrInst*>(inst)) {
if (value_vreg_map.count(cond_br->getCondition()))
use_set.insert(value_vreg_map.at(cond_br->getCondition()));
}
// AllocaInst 不直接“使用”或“定义”虚拟寄存器,其地址是常量。
// 计算新的 live_in = use U (new_live_out - def)
std::set<std::string> new_live_in = use_set;
@@ -1041,26 +991,38 @@ RISCv32CodeGen::RegAllocResult RISCv32CodeGen::register_allocation(Function* fun
eliminate_phi(func); // 确保首先调用此函数
// 为每个函数重置计数器
alloca_offset_counter = 0;
vreg_counter = 0;
value_vreg_map.clear(); // 为每个函数清除
// 活跃性分析之前,为 alloca 指令和函数参数分配虚拟寄存器
// 并为 allocas 建立初始栈映射
RegAllocResult alloc_result;
// FIX: 在进行活跃性分析之前,为所有产生值的指令分配虚拟寄存器
// 这确保了活跃性分析和寄存器分配器有可操作的虚拟寄存器
for (const auto& bb_ptr : func->getBasicBlocks()) {
for (const auto& inst_ptr : bb_ptr->getInstructions()) {
Instruction* inst = inst_ptr.get();
// 如果指令产生一个非 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++);
}
}
// 也为常量操作数分配vreg以便它们可以参与活跃性分析
for(const auto& operand_use : inst->getOperands()){
Value* operand = operand_use->getValue();
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++);
}
}
}
}
}
// 为所有产生值的指令分配虚拟寄存器。
// 这部分实际上在 build_dag 中发生。
// 但是,为了使活跃性分析工作,所有可能使用的 Value* 都必须有一个虚拟寄存器。
// 我们可以遍历指令 (在 DAG 构建之前) 来填充 `value_vreg_map`。
// 如果 DAG 分配虚拟寄存器,这有点像先有鸡还是先有蛋的问题。
// 让我们假设 build_dag 在分配虚拟寄存器时填充 value_vreg_map。
RegAllocResult alloc_result;
// 计算 AllocaInst 的栈偏移量
int current_stack_offset = 0; // 相对于 s0 (帧指针)
// 参数由 a0-a7 处理,所以除非它们溢出,否则这里不需要直接为它们分配栈空间。
// 收集函数中所有唯一的 AllocaInst
std::set<AllocaInst*> allocas_in_func;
for (const auto& bb_ptr : func->getBasicBlocks()) {
for (const auto& inst_ptr : bb_ptr->getInstructions()) {
@@ -1083,8 +1045,6 @@ RISCv32CodeGen::RegAllocResult RISCv32CodeGen::register_allocation(Function* fun
// s0 在 (stack_size - 8)(sp)
// 所以最小栈大小必须是 8 + current_stack_offset。
alloc_result.stack_size = current_stack_offset + 8; // 用于 s0 和 ra
// 对齐到 16 字节以符合 ABI
alloc_result.stack_size = (alloc_result.stack_size + 15) & ~15;
// 2. 活跃性分析
std::map<Instruction*, std::set<std::string>> live_sets = liveness_analysis(func);
@@ -1095,6 +1055,9 @@ RISCv32CodeGen::RegAllocResult RISCv32CodeGen::register_allocation(Function* fun
// 4. 图着色
color_graph(alloc_result.vreg_to_preg, interference_graph);
// 完整的溢出处理逻辑比较复杂,这里暂时省略。
// 如果一个vreg没有被着色get_preg_or_temp会回退到t0这对于简单情况可能够用。
return alloc_result;
}

View File

@@ -5,7 +5,7 @@ int main() {
const int b = 2;
int c;
if (a == b)
if (a != b)
c = b - a + 20; // 21 <- this
else
c = a * b + b + b + 10; // 16