diff --git a/src/RISCv64Backend.cpp b/src/RISCv64Backend.cpp index e07d04d..3bbc148 100644 --- a/src/RISCv64Backend.cpp +++ b/src/RISCv64Backend.cpp @@ -574,10 +574,7 @@ void RISCv64CodeGen::print_dag(const std::vector>& dag, // 指令选择 void RISCv64CodeGen::select_instructions(DAGNode* node, const RegAllocResult& alloc) { if (!node) return; - // 为确保我们的新逻辑被应用,即使节点已被访问,我们仍然可能需要重新生成指令。 - // 但是,为了防止无限递归,我们依赖于调用者(emit_instructions)的逻辑。 - // 最关键的改动是,指令的“使用者”将负责加载其操作数。 - if (!node->inst.empty()) return; // 保持原有逻辑,避免重复工作 + if (!node->inst.empty()) return; // 指令已选择,跳过重复处理 // 递归地为操作数选择指令,确保依赖先被处理 for (auto operand : node->operands) { @@ -588,8 +585,9 @@ void RISCv64CodeGen::select_instructions(DAGNode* node, const RegAllocResult& al std::stringstream ss_inst; // 使用 stringstream 构建指令 + // 获取分配的物理寄存器,若未分配则回退到 t0 auto get_preg_or_temp = [&](const std::string& vreg) { - if (vreg.empty()) { + if (vreg.empty()) { // 添加对空 vreg 的明确检查 if (DEBUG) std::cerr << "警告: 虚拟寄存器 (空字符串) 没有分配物理寄存器,使用临时寄存器 t0 代替。\n"; return reg_to_string(PhysicalReg::T0); } @@ -597,31 +595,35 @@ void RISCv64CodeGen::select_instructions(DAGNode* node, const RegAllocResult& al return reg_to_string(alloc.vreg_to_preg.at(vreg)); } if (DEBUG) std::cerr << "警告: 虚拟寄存器 " << vreg << " 没有分配物理寄存器,使用临时寄存器 t0 代替。\n"; - return reg_to_string(PhysicalReg::T0); + return reg_to_string(PhysicalReg::T0); // 回退到临时寄存器 t0 }; - auto get_stack_offset = [&](Value* val) -> std::string { + // 获取栈变量的内存偏移量 + auto get_stack_offset = [&](Value* val) -> std::string { // 返回类型明确为 std::string if (alloc.stack_map.count(val)) { + if (DEBUG) { // 避免在非DEBUG模式下打印大量内容 + std::cout << "获取栈变量的内存偏移量,变量名: " << (val ? val->getName() : "unknown") << std::endl; + } return std::to_string(alloc.stack_map.at(val)); } - return std::string("0"); + if (DEBUG) std::cerr << "警告: 栈变量 " << (val ? val->getName() : "unknown") << " 没有在栈映射中找到,使用默认偏移 0。\n"; + // 如果没有找到映射,返回默认偏移量 "0" + return std::string("0"); // 默认或错误情况 }; switch (node->kind) { - // ====================== 方案二核心修改点 1 ====================== case DAGNode::CONSTANT: { - // CONSTANT 节点自身不再生成指令。 - // 它的存在是为了在DAG中标记一个常数值。 - // 加载这个值的责任转移给了使用它的节点。 + // [V2 特性] CONSTANT 节点自身不再生成指令。 + // 它的存在是为了在DAG中标记一个常数值或全局地址。 + // 加载这个值的责任转移给了使用它的节点 (STORE, BINARY, CALL, RETURN)。 break; } - // ============================================================= - case DAGNode::ALLOCA_ADDR: { - // ALLOCA_ADDR 节点不直接生成指令,由 LOAD/STORE 使用 + // ALLOCA_ADDR 节点不直接生成指令,它仅作为一个地址标记,由 LOAD/STORE 或地址计算使用 break; } case DAGNode::LOAD: { + // 处理加载指令 if (node->operands.empty() || !node->operands[0]) break; std::string dest_reg = get_preg_or_temp(node->result_vreg); DAGNode* ptr_node = node->operands[0]; @@ -638,24 +640,31 @@ void RISCv64CodeGen::select_instructions(DAGNode* node, const RegAllocResult& al break; } case DAGNode::STORE: { + // 处理存储指令 if (node->operands.size() < 2 || !node->operands[0] || !node->operands[1]) break; DAGNode* val_node = node->operands[0]; DAGNode* ptr_node = node->operands[1]; std::string src_reg = get_preg_or_temp(val_node->result_vreg); - // ====================== 方案二核心修改点 2 ====================== - // 如果要存储的值是一个常数,在此处立即加载它 + // [V2 特性] 如果要存储的值是一个常数,在此处立即加载它 if (val_node->kind == DAGNode::CONSTANT) { if (auto constant = dynamic_cast(val_node->value)) { - ss_inst << "li " << src_reg << ", " << constant->getInt() << "\n "; + if (constant->isInt()) { + ss_inst << "li " << src_reg << ", " << constant->getInt() << "\n\t"; + } else { // 浮点数常量 + float f = constant->getFloat(); + uint32_t float_bits = *(uint32_t*)&f; + ss_inst << "li " << src_reg << ", " << float_bits << "\n\t"; + // 注意:这里假设 src_reg 是一个通用寄存器,需要额外指令移动到浮点寄存器 + // 如果 STORE 的目标是浮点数,这里逻辑需要调整 + } } else if (auto global = dynamic_cast(val_node->value)) { // 如果是存储全局变量的地址 - ss_inst << "la " << src_reg << ", " << global->getName() << "\n "; + ss_inst << "la " << src_reg << ", " << global->getName() << "\n\t"; } } - // ============================================================= - + if (ptr_node->kind == DAGNode::ALLOCA_ADDR) { if (auto alloca_inst = dynamic_cast(ptr_node->value)) { int offset = alloc.stack_map.at(alloca_inst); @@ -676,32 +685,7 @@ void RISCv64CodeGen::select_instructions(DAGNode* node, const RegAllocResult& al DAGNode* lhs_node = node->operands[0]; DAGNode* rhs_node = node->operands[1]; - std::string lhs_reg = get_preg_or_temp(lhs_node->result_vreg); - std::string rhs_reg = get_preg_or_temp(rhs_node->result_vreg); - - // ====================== 方案二核心修改点 3 ====================== - // 在生成二元运算指令前,检查操作数是否为常数,并按需加载 - if (lhs_node->kind == DAGNode::CONSTANT) { - if (auto c = dynamic_cast(lhs_node->value)) { - ss_inst << "li " << lhs_reg << ", " << c->getInt() << "\n "; - } - } - - if (rhs_node->kind == DAGNode::CONSTANT) { - if (auto c = dynamic_cast(rhs_node->value)) { - // 优化:对于加法和小立即数,使用 addi - if (bin->getKind() == BinaryInst::kAdd && c->getInt() >= -2048 && c->getInt() < 2048) { - ss_inst << "addi " << dest_reg << ", " << lhs_reg << ", " << c->getInt(); - node->inst = ss_inst.str(); - return; // 指令已生成,提前返回 - } - // 否则,正常加载 - ss_inst << "li " << rhs_reg << ", " << c->getInt() << "\n "; - } - } - // ============================================================= - - // 检查是否是 base + offset 的地址计算 + // [V1 特性] 检查是否是 base + offset 的地址计算 if (bin->getKind() == BinaryInst::kAdd) { DAGNode* base_node = nullptr; DAGNode* offset_node = nullptr; @@ -711,31 +695,65 @@ void RISCv64CodeGen::select_instructions(DAGNode* node, const RegAllocResult& al if (base_node) { if (auto alloca_inst = dynamic_cast(base_node->value)) { - std::string offset_str = get_stack_offset(alloca_inst); - std::string offset_reg_str = get_preg_or_temp(offset_node->result_vreg); - ss_inst << "addi " << dest_reg << ", s0, " << offset_str << "\n"; - ss_inst << " addw " << dest_reg << ", " << dest_reg << ", " << offset_reg_str; + std::string stack_offset_str = get_stack_offset(alloca_inst); + std::string index_offset_reg = get_preg_or_temp(offset_node->result_vreg); + + // 生成两条指令来计算最终地址: + // 1. addi 将 s0 加上 b 的栈偏移量得到 b 的实际基地址 + ss_inst << "addi " << dest_reg << ", s0, " << stack_offset_str << "\n"; + // 2. [V3 修复] add 将基地址和索引偏移量相加,得到最终地址。地址计算必须用64位指令。 + ss_inst << "\tadd " << dest_reg << ", " << dest_reg << ", " << index_offset_reg; node->inst = ss_inst.str(); - return; + return; // 指令已生成,提前返回 } } } + std::string lhs_reg = get_preg_or_temp(lhs_node->result_vreg); + std::string rhs_reg = get_preg_or_temp(rhs_node->result_vreg); + + // [V2 特性] 在生成二元运算指令前,检查操作数是否为常数,并按需加载 + if (lhs_node->kind == DAGNode::CONSTANT) { + if (auto c = dynamic_cast(lhs_node->value)) { + ss_inst << "li " << lhs_reg << ", " << c->getInt() << "\n\t"; + } + } + if (rhs_node->kind == DAGNode::CONSTANT) { + if (auto c = dynamic_cast(rhs_node->value)) { + // 优化:对于加法和小立即数,直接使用 addi 指令 + if (bin->getKind() == BinaryInst::kAdd && c->getInt() >= -2048 && c->getInt() < 2048) { + ss_inst << "addi " << dest_reg << ", " << lhs_reg << ", " << c->getInt(); + node->inst = ss_inst.str(); + return; // 指令已生成,提前返回 + } + // 否则,正常加载 + ss_inst << "li " << rhs_reg << ", " << c->getInt() << "\n\t"; + } + } + std::string opcode; switch (bin->getKind()) { - case BinaryInst::kAdd: opcode = "addw"; break; + // [V1 特性] & [V3 修复] RV64 修改: 整数运算使用带 'w' 后缀的32位指令,地址运算使用64位指令 + case BinaryInst::kAdd: + // 通过检查操作数类型来决定使用 64位(add) 还是 32位(addw) 加法 + if (bin->getLhs()->getType()->isPointer() || bin->getRhs()->getType()->isPointer()) { + opcode = "add"; // 指针/地址运算 + } else { + opcode = "addw"; // 普通整数运算 + } + break; case BinaryInst::kSub: opcode = "subw"; break; case BinaryInst::kMul: opcode = "mulw"; break; case Instruction::kDiv: opcode = "divw"; break; case Instruction::kRem: opcode = "remw"; break; case BinaryInst::kICmpEQ: ss_inst << "subw " << dest_reg << ", " << lhs_reg << ", " << rhs_reg << "\n"; - ss_inst << " seqz " << dest_reg << ", " << dest_reg; + ss_inst << "\tseqz " << dest_reg << ", " << dest_reg; node->inst = ss_inst.str(); return; case BinaryInst::kICmpGE: ss_inst << "slt " << dest_reg << ", " << lhs_reg << ", " << rhs_reg << "\n"; - ss_inst << " xori " << dest_reg << ", " << dest_reg << ", 1"; + ss_inst << "\txori " << dest_reg << ", " << dest_reg << ", 1"; node->inst = ss_inst.str(); return; case BinaryInst::kICmpGT: @@ -744,7 +762,7 @@ void RISCv64CodeGen::select_instructions(DAGNode* node, const RegAllocResult& al return; case BinaryInst::kICmpLE: ss_inst << "slt " << dest_reg << ", " << rhs_reg << ", " << lhs_reg << "\n"; - ss_inst << " xori " << dest_reg << ", " << dest_reg << ", 1"; + ss_inst << "\txori " << dest_reg << ", " << dest_reg << ", 1"; node->inst = ss_inst.str(); return; case BinaryInst::kICmpLT: @@ -753,10 +771,11 @@ void RISCv64CodeGen::select_instructions(DAGNode* node, const RegAllocResult& al return; case BinaryInst::kICmpNE: ss_inst << "subw " << dest_reg << ", " << lhs_reg << ", " << rhs_reg << "\n"; - ss_inst << " snez " << dest_reg << ", " << dest_reg; + ss_inst << "\tsnez " << dest_reg << ", " << dest_reg; node->inst = ss_inst.str(); return; default: + // [V1 特性] 保留对未实现指令的定义和报错 throw std::runtime_error("不支持的二元指令类型: " + bin->getKindString()); } if (!opcode.empty()) { @@ -774,100 +793,122 @@ void RISCv64CodeGen::select_instructions(DAGNode* node, const RegAllocResult& al switch (unary->getKind()) { case UnaryInst::kNeg: + // RV64 修改: 使用 subw 实现32位取负 (negw 伪指令) ss_inst << "subw " << dest_reg << ", x0, " << src_reg; break; case UnaryInst::kNot: + // 整数逻辑非:seqz rd, rs (rs == 0 时 rd = 1,否则 rd = 0) ss_inst << "seqz " << dest_reg << ", " << src_reg; break; + // [V1 特性] 保留对未实现指令的定义和报错 + case UnaryInst::kFNeg: + case UnaryInst::kFNot: + case UnaryInst::kFtoI: + case UnaryInst::kItoF: + case UnaryInst::kBitFtoI: + case UnaryInst::kBitItoF: + throw std::runtime_error("不支持的浮点一元指令类型: " + unary->getKindString()); default: throw std::runtime_error("不支持的一元指令类型: " + unary->getKindString()); } break; } case DAGNode::CALL: { + // 处理函数调用指令 if (!node->value) break; auto call = dynamic_cast(node->value); if (!call) break; - // ====================== 此处是修正点 ====================== + // [V2/V3 特性] 修正参数处理逻辑 for (size_t i = 0; i < node->operands.size() && i < 8; ++i) { DAGNode* arg_node = node->operands[i]; if (!arg_node) continue; std::string arg_preg = reg_to_string(static_cast(static_cast(PhysicalReg::A0) + i)); - // 优先检查参数节点是否为常量 + // 优先检查参数节点是否为常量,并直接加载 if (arg_node->kind == DAGNode::CONSTANT) { if (auto const_val = dynamic_cast(arg_node->value)) { - // 如果是常量,直接生成 li 指令加载到参数寄存器 - ss_inst << "li " << arg_preg << ", " << const_val->getInt() << "\n "; + ss_inst << "li " << arg_preg << ", " << const_val->getInt() << "\n\t"; } else if (auto global_val = dynamic_cast(arg_node->value)) { - // 如果是全局变量地址,生成 la 指令 - ss_inst << "la " << arg_preg << ", " << global_val->getName() << "\n "; + ss_inst << "la " << arg_preg << ", " << global_val->getName() << "\n\t"; } } // 如果不是常量,说明是一个计算出的值,使用 mv 指令移动 else { std::string src_reg = get_preg_or_temp(arg_node->result_vreg); - ss_inst << "mv " << arg_preg << ", " << src_reg << "\n "; + ss_inst << "mv " << arg_preg << ", " << src_reg << "\n\t"; } } ss_inst << "call " << call->getCallee()->getName(); + // 处理返回值 if ((call->getType()->isInt() || call->getType()->isFloat()) && !node->result_vreg.empty()) { - ss_inst << "\n mv " << get_preg_or_temp(node->result_vreg) << ", a0"; + ss_inst << "\nmv " << get_preg_or_temp(node->result_vreg) << ", a0"; } break; } case DAGNode::RETURN: { + // 处理返回指令 if (!node->operands.empty() && node->operands[0]) { DAGNode* ret_val_node = node->operands[0]; - std::string return_val_reg = get_preg_or_temp(ret_val_node->result_vreg); - // ====================== 方案二核心修改点 4 ====================== + // [V2 特性] 如果返回值是常量,直接加载到 a0 if (ret_val_node->kind == DAGNode::CONSTANT) { if (auto c = dynamic_cast(ret_val_node->value)) { ss_inst << "li a0, " << c->getInt() << "\n"; } } else { + std::string return_val_reg = get_preg_or_temp(ret_val_node->result_vreg); ss_inst << "mv a0, " << return_val_reg << "\n"; } - // ============================================================= } + // 恢复栈帧 if (alloc.stack_size > 0) { int aligned_stack_size = (alloc.stack_size + 15) & ~15; - ss_inst << " ld ra, " << (aligned_stack_size - 8) << "(sp)\n"; - ss_inst << " ld s0, " << (aligned_stack_size - 16) << "(sp)\n"; - ss_inst << " addi sp, sp, " << aligned_stack_size << "\n"; + // RV64 修改: 使用 ld (load doubleword) 恢复 8 字节的 ra 和 s0 + ss_inst << "\tld ra, " << (aligned_stack_size - 8) << "(sp)\n"; + ss_inst << "\tld s0, " << (aligned_stack_size - 16) << "(sp)\n"; + ss_inst << "\taddi sp, sp, " << aligned_stack_size << "\n"; } - ss_inst << " ret"; + ss_inst << "\tret"; break; } case DAGNode::BRANCH: { - // BRANCH 节点的现有逻辑是正确的,保持不变 + // 处理分支指令 auto br = dynamic_cast(node->value); auto uncond_br = dynamic_cast(node->value); - if (node->inst.empty()) { - if (br) { - if (node->operands.empty() || !node->operands[0]) break; - std::string cond_reg = get_preg_or_temp(node->operands[0]->result_vreg); - std::string then_block = br->getThenBlock()->getName(); - std::string else_block = br->getElseBlock()->getName(); - ss_inst << "bnez " << cond_reg << ", " << then_block << "\n"; - ss_inst << " j " << else_block; - } else if (uncond_br) { - ss_inst << "j " << uncond_br->getBlock()->getName(); + + if (br) { // 条件分支 + if (node->operands.empty() || !node->operands[0]) break; + std::string cond_reg = get_preg_or_temp(node->operands[0]->result_vreg); + std::string then_block = br->getThenBlock()->getName(); + std::string else_block = br->getElseBlock()->getName(); + + // [V1 特性] 如果基本块没有名字(例如,匿名块),给它一个伪名称 + if (then_block.empty()) { + then_block = ENTRY_BLOCK_PSEUDO_NAME + "_then_" + std::to_string((uintptr_t)br); } - } else { - ss_inst << node->inst; + if (else_block.empty()) { + else_block = ENTRY_BLOCK_PSEUDO_NAME + "_else_" + std::to_string((uintptr_t)br); + } + + ss_inst << "bnez " << cond_reg << ", " << then_block << "\n"; + ss_inst << "\tj " << else_block; + } else if (uncond_br) { // 无条件分支 + std::string target_block = uncond_br->getBlock()->getName(); + if (target_block.empty()) { + target_block = ENTRY_BLOCK_PSEUDO_NAME + "_target_" + std::to_string((uintptr_t)uncond_br); + } + ss_inst << "j " << target_block; } break; } default: throw std::runtime_error("不支持的节点类型: " + node->getNodeKindString()); } - node->inst = ss_inst.str(); + node->inst = ss_inst.str(); // 存储生成的指令 } // 指令发射