From 11cd32e6dfb2311b87689924514192559f41748a Mon Sep 17 00:00:00 2001 From: Lixuanwang Date: Tue, 24 Jun 2025 00:35:38 +0800 Subject: [PATCH] [backend] fixed some bugs --- src/RISCv32Backend.cpp | 744 +++++++++++++++++++++++------------------ src/RISCv32Backend.h | 13 +- 2 files changed, 426 insertions(+), 331 deletions(-) diff --git a/src/RISCv32Backend.cpp b/src/RISCv32Backend.cpp index 76ea80a..ff108ec 100644 --- a/src/RISCv32Backend.cpp +++ b/src/RISCv32Backend.cpp @@ -8,7 +8,7 @@ namespace sysy { -// Available general-purpose registers for allocation +// 可用于分配的通用寄存器 const std::vector RISCv32CodeGen::allocable_regs = { PhysicalReg::T0, PhysicalReg::T1, PhysicalReg::T2, PhysicalReg::T3, PhysicalReg::T4, PhysicalReg::T5, PhysicalReg::T6, @@ -19,58 +19,61 @@ const std::vector RISCv32CodeGen::allocable_regs = PhysicalReg::S8, PhysicalReg::S9, PhysicalReg::S10, PhysicalReg::S11 }; +// 将物理寄存器枚举转换为字符串 std::string RISCv32CodeGen::reg_to_string(PhysicalReg reg) { switch (reg) { - case PhysicalReg::ZERO: return "x0"; // zero register - case PhysicalReg::RA: return "ra"; - case PhysicalReg::SP: return "sp"; - case PhysicalReg::GP: return "gp"; - case PhysicalReg::TP: return "tp"; - case PhysicalReg::T0: return "t0"; - case PhysicalReg::T1: return "t1"; - case PhysicalReg::T2: return "t2"; - case PhysicalReg::S0: return "s0"; - case PhysicalReg::S1: return "s1"; - case PhysicalReg::A0: return "a0"; - case PhysicalReg::A1: return "a1"; - case PhysicalReg::A2: return "a2"; - case PhysicalReg::A3: return "a3"; - case PhysicalReg::A4: return "a4"; - case PhysicalReg::A5: return "a5"; - case PhysicalReg::A6: return "a6"; - case PhysicalReg::A7: return "a7"; - case PhysicalReg::S2: return "s2"; - case PhysicalReg::S3: return "s3"; - case PhysicalReg::S4: return "s4"; - case PhysicalReg::S5: return "s5"; - case PhysicalReg::S6: return "s6"; - case PhysicalReg::S7: return "s7"; - case PhysicalReg::S8: return "s8"; - case PhysicalReg::S9: return "s9"; - case PhysicalReg::S10: return "s10"; - case PhysicalReg::S11: return "s11"; - case PhysicalReg::T3: return "t3"; - case PhysicalReg::T4: return "t4"; - case PhysicalReg::T5: return "t5"; - case PhysicalReg::T6: return "t6"; + case PhysicalReg::ZERO: return "x0"; // 零寄存器 + case PhysicalReg::RA: return "ra"; // 返回地址寄存器 + case PhysicalReg::SP: return "sp"; // 栈指针 + case PhysicalReg::GP: return "gp"; // 全局指针 + case PhysicalReg::TP: return "tp"; // 线程指针 + case PhysicalReg::T0: return "t0"; // 临时寄存器 + case PhysicalReg::T1: return "t1"; // 临时寄存器 + case PhysicalReg::T2: return "t2"; // 临时寄存器 + case PhysicalReg::S0: return "s0"; // 帧指针 / 保存的寄存器 + case PhysicalReg::S1: return "s1"; // 保存的寄存器 + case PhysicalReg::A0: return "a0"; // 函数参数 / 返回值寄存器 + case PhysicalReg::A1: return "a1"; // 函数参数 + case PhysicalReg::A2: return "a2"; // 函数参数 + case PhysicalReg::A3: return "a3"; // 函数参数 + case PhysicalReg::A4: return "a4"; // 函数参数 + case PhysicalReg::A5: return "a5"; // 函数参数 + case PhysicalReg::A6: return "a6"; // 函数参数 + case PhysicalReg::A7: return "a7"; // 函数参数 + case PhysicalReg::S2: return "s2"; // 保存的寄存器 + case PhysicalReg::S3: return "s3"; // 保存的寄存器 + case PhysicalReg::S4: return "s4"; // 保存的寄存器 + case PhysicalReg::S5: return "s5"; // 保存的寄存器 + case PhysicalReg::S6: return "s6"; // 保存的寄存器 + case PhysicalReg::S7: return "s7"; // 保存的寄存器 + case PhysicalReg::S8: return "s8"; // 保存的寄存器 + case PhysicalReg::S9: return "s9"; // 保存的寄存器 + case PhysicalReg::S10: return "s10"; // 保存的寄存器 + case PhysicalReg::S11: return "s11"; // 保存的寄存器 + case PhysicalReg::T3: return "t3"; // 临时寄存器 + case PhysicalReg::T4: return "t4"; // 临时寄存器 + case PhysicalReg::T5: return "t5"; // 临时寄存器 + case PhysicalReg::T6: return "t6"; // 临时寄存器 default: return "UNKNOWN_REG"; } } +// 总体代码生成入口 std::string RISCv32CodeGen::code_gen() { std::stringstream ss; ss << module_gen(); return ss.str(); } +// 模块级代码生成 (处理全局变量和函数) std::string RISCv32CodeGen::module_gen() { std::stringstream ss; bool has_globals = !module->getGlobals().empty(); if (has_globals) { - ss << ".data\n"; + ss << ".data\n"; // 数据段 for (const auto& global : module->getGlobals()) { - ss << ".globl " << global->getName() << "\n"; - ss << global->getName() << ":\n"; + ss << ".globl " << global->getName() << "\n"; // 声明全局符号 + ss << global->getName() << ":\n"; // 标签 const auto& init_values = global->getInitValues(); for (size_t i = 0; i < init_values.getValues().size(); ++i) { auto val = init_values.getValues()[i]; @@ -78,11 +81,11 @@ std::string RISCv32CodeGen::module_gen() { if (auto constant = dynamic_cast(val)) { for (unsigned j = 0; j < count; ++j) { if (constant->isInt()) { - ss << " .word " << constant->getInt() << "\n"; + ss << " .word " << constant->getInt() << "\n"; // 整数常量 } else { float f = constant->getFloat(); uint32_t float_bits = *(uint32_t*)&f; - ss << " .word " << float_bits << "\n"; + ss << " .word " << float_bits << "\n"; // 浮点常量 (按位存储) } } } @@ -90,7 +93,7 @@ std::string RISCv32CodeGen::module_gen() { } } if (!module->getFunctions().empty()) { - ss << ".text\n"; + ss << ".text\n"; // 代码段 for (const auto& func : module->getFunctions()) { ss << function_gen(func.second.get()); } @@ -98,92 +101,159 @@ std::string RISCv32CodeGen::module_gen() { return ss.str(); } +// 函数级代码生成 std::string RISCv32CodeGen::function_gen(Function* func) { std::stringstream ss; - ss << ".globl " << func->getName() << "\n"; - ss << func->getName() << ":\n"; + ss << ".globl " << func->getName() << "\n"; // 声明函数为全局符号 + ss << func->getName() << ":\n"; // 函数入口标签 - // Perform register allocation for the entire function + // 执行寄存器分配 auto alloc = register_allocation(func); int stack_size = alloc.stack_size; - // Prologue - // Save ra and s0, adjust stack pointer - // s0 points to the base of the current frame (sp after allocation for locals/spills) - // Stack layout: - // +------------+ high address - // | Arg Spill | + // 函数序言 (Prologue) + // 保存 ra 和 s0, 调整栈指针 + // s0 指向当前帧的底部(分配局部变量/溢出空间后的 sp) + // 栈布局: + // +------------+ 高地址 + // | 参数溢出 | (如果参数超过 8 个) // +------------+ - // | Saved RA | (stack_size - 4)(sp) + // | 保存的 RA | (stack_size - 4)(sp) // +------------+ - // | Saved S0 | (stack_size - 8)(sp) + // | 保存的 S0 | (stack_size - 8)(sp) // +------------+ - // | Locals/Spills | 0(sp) to (stack_size - 8 - 4*num_locals)(sp) - // +------------+ low address - // The given IR only has one alloca, so the stack_size should be at least 8 (for ra, s0) + 4 (for %c(0)) = 12, - // rounded up to 16 for 16-byte alignment, or 32 as in your example. - // For simplicity, let's keep it at a multiple of 16 for alignment. - int aligned_stack_size = (stack_size + 15) & ~15; // Align to 16 bytes + // | 局部变量/溢出 | 0(sp) 到 (stack_size - 8 - 局部变量大小)(sp) + // +------------+ 低地址 + // 确保栈大小 16 字节对齐 + int aligned_stack_size = (stack_size + 15) & ~15; + // 只有当需要栈空间时才生成序言 if (aligned_stack_size > 0) { - ss << " addi sp, sp, -" << aligned_stack_size << "\n"; - ss << " sw ra, " << (aligned_stack_size - 4) << "(sp)\n"; - ss << " sw s0, " << (aligned_stack_size - 8) << "(sp)\n"; - ss << " mv s0, sp\n"; // Frame pointer points to new stack top + ss << " addi sp, sp, -" << aligned_stack_size << "\n"; // 调整栈指针 + ss << " sw ra, " << (aligned_stack_size - 4) << "(sp)\n"; // 保存返回地址 + ss << " sw s0, " << (aligned_stack_size - 8) << "(sp)\n"; // 保存帧指针 + ss << " mv s0, sp\n"; // 设置新的帧指针 } - // Generate code for each basic block + // 生成每个基本块的代码 + int block_idx = 0; // 用于生成默认 entry 标签的索引 for (const auto& bb : func->getBasicBlocks()) { - ss << basicBlock_gen(bb.get(), alloc); + ss << basicBlock_gen(bb.get(), alloc, block_idx++); } - // Epilogue is handled by the RETURN DAGNode's instruction selection + // 函数尾声 (Epilogue) 由 RETURN DAGNode 的指令选择处理,确保正确恢复栈和寄存器 return ss.str(); } -std::string RISCv32CodeGen::basicBlock_gen(BasicBlock* bb, const RegAllocResult& alloc) { +// 基本块代码生成 +std::string RISCv32CodeGen::basicBlock_gen(BasicBlock* bb, const RegAllocResult& alloc, int block_idx) { std::stringstream ss; - ss << bb->getName() << ":\n"; - // Clear value_vreg_map for each basic block to prevent virtual register reuse across blocks, - // unless they are explicitly live-in. This simplifies DAG building for single basic blocks. - value_vreg_map.clear(); - vreg_counter = 0; // Reset virtual register counter for each block - - auto dag = build_dag(bb); - print_dag(dag, bb->getName()); // Print DAG for debugging - - std::vector insts; - std::set emitted_nodes; // Track emitted nodes to prevent duplicates - - // Emit instructions in reverse topological order (or dependency order) - // The `emit_instructions` function handles the recursive emission of operands. - for (auto it = dag.rbegin(); it != dag.rend(); ++it) { - select_instructions(it->get(), alloc); // Select instruction for this node - emit_instructions(it->get(), insts, alloc, emitted_nodes); // Emit this node and its dependencies - } - - // Since `emit_instructions` adds in reverse order of emission (dependencies first), - // we need to reverse the collected instructions to get proper execution order. - std::reverse(insts.begin(), insts.end()); - - for (const auto& inst : insts) { - if (!inst.empty()) { // Ensure no empty lines are added - ss << " " << inst << "\n"; + // 修复空标签问题:如果块名为空,使用伪名称 (例如 entry_block_0) + std::string bb_name = bb->getName(); + if (bb_name.empty()) { + bb_name = ENTRY_BLOCK_PSEUDO_NAME + std::to_string(block_idx); + // 如果是第一个块,且没有名称,则通常是函数入口块,可以考虑直接使用 "entry" + // 但为了通用性,伪名称更安全 + if (block_idx == 0) { // 第一个块通常是 entry 块 + bb_name = "entry"; } } + ss << bb_name << ":\n"; // 基本块标签 + + // 为每个基本块重置虚拟寄存器映射和计数器,因为 DAG 是按块构建的 + // 注意:这里的重置可能会影响跨块的活跃性分析,如果 DAG 构建是跨块的,则不能在这里重置 + // 但目前 DAG 是按块构建,所以在此重置是合理的。 + value_vreg_map.clear(); + vreg_counter = 0; // 为每个块重置虚拟寄存器计数器 + + // 构建当前基本块的 DAG + // 注意:DAGNode 的唯一性在当前函数范围内是重要的, + // 所以 build_dag 应该返回一个完整的 DAG 节点列表,而不是每次都创建新的。 + // 为了简化,这里仍然按块构建,但需要注意跨块值的使用。 + auto dag_nodes_for_bb = build_dag(bb); + print_dag(dag_nodes_for_bb, bb_name); // 打印 DAG 调试信息 + + // 存储最终生成的指令 + std::set emitted_nodes; // 跟踪已发射的节点,防止重复 + std::vector ordered_insts; // 用于收集指令并按序排列 + + // 在 DAG 中遍历并生成指令。由于 select_instructions 可能会递归地为操作数选择指令, + // 并且 emit_instructions 也会递归地发射,我们需要一个机制来确保指令的正确顺序和唯一性。 + // 最简单的方法是逆拓扑序遍历所有节点,确保其操作数先被处理。 + // 但是目前的 DAG 构建方式可能不支持直接的拓扑排序, + // 我们将依赖 emit_instructions 的递归特性来处理依赖。 + + // 遍历 DAG 的根节点(没有用户的节点,或者 Store/Return/Branch 节点) + // 从这些节点开始递归发射指令。 + // NOTE: 这种发射方式可能不总是产生最优的代码顺序,但可以确保依赖关系。 + for (auto it = dag_nodes_for_bb.rbegin(); it != dag_nodes_for_bb.rend(); ++it) { + DAGNode* node = it->get(); + // 只有那些没有用户(或者代表副作用,如STORE, RETURN, BRANCH)的节点才需要作为发射的“根” + // 否则,它们会被其用户节点递归地发射 + // 然而,为了确保所有指令都被发射,我们通常从所有节点(或者至少是副作用节点)开始发射 + // 并且利用 emitted_nodes 集合防止重复 + // 这里简化为对所有 DAG 节点进行一次 select_instructions 和 emit_instructions 调用。 + // emit_instructions 会通过递归处理其操作数来保证依赖顺序。 + select_instructions(node, alloc); // 为当前节点选择指令 + } + + // 收集所有指令到一个临时的 vector 中,然后进行排序 + // 注意:这里的发射逻辑需要重新设计,目前的 emit_instructions 是直接添加到 std::vector& insts 中 + // 并且期望是按顺序添加的,这在递归时难以保证。 + // 更好的方法是让 emit_instructions 直接输出到 stringstream,并控制递归顺序。 + // 但是为了最小化改动,我们先保持 emit_instructions 的现有签名, + // 然后在它内部处理指令的收集和去重。 + + // 重新设计 emit_instructions 的调用方式 + // 这里的思路是,每个 DAGNode 都存储了自己及其依赖(如果未被其他节点引用)的指令。 + // 最终,我们遍历 BasicBlock 中的所有原始 IR 指令,找到它们对应的 DAGNode,然后发射。 + // 这是因为 IR 指令的顺序决定了代码的逻辑顺序。 + + // 遍历 IR 指令,并找到对应的 DAGNode 进行发射 + // 由于 build_dag 是从 IR 指令顺序构建的,我们应该按照 IR 指令的顺序来发射。 + emitted_nodes.clear(); // 再次清空已发射节点集合 + // 临时存储每个 IR 指令对应的 DAGNode,因为 DAGNode 列表是平铺的 + std::map inst_to_dag_node; + for (const auto& dag_node_ptr : dag_nodes_for_bb) { + if (dag_node_ptr->value && dynamic_cast(dag_node_ptr->value)) { + inst_to_dag_node[dynamic_cast(dag_node_ptr->value)] = dag_node_ptr.get(); + } + } + + for (const auto& inst_ptr : bb->getInstructions()) { + DAGNode* node_to_emit = nullptr; + // 查找当前 IR 指令在 DAG 中对应的节点。 + // 注意:不是所有 IR 指令都会直接映射到一个“根”DAGNode (例如,某些值可能只作为操作数存在) + // 但终结符(如 Branch, Return)和 Store 指令总是重要的。 + // 对于 load/binary 等,我们应该在 build_dag 中确保它们有一个结果 vreg,并被后续指令使用。 + // 如果一个 IR 指令是某个 DAGNode 的 value,那么我们就发射那个 DAGNode。 + if (inst_to_dag_node.count(inst_ptr.get())) { + node_to_emit = inst_to_dag_node.at(inst_ptr.get()); + } + + if (node_to_emit) { + // 注意:select_instructions 已经在上面统一调用过,这里只需要 emit。 + // 但如果 select_instructions 没有递归地为所有依赖选择指令,这里可能需要重新考虑。 + // 为了简化,我们假定 select_instructions 在第一次被调用时(通常在 emit 之前)已经递归地为所有操作数选择了指令。 + + // 直接将指令添加到 ss 中,而不是通过 vector 中转 + emit_instructions(node_to_emit, ss, alloc, emitted_nodes); + } + } + return ss.str(); } // DAG 构建 std::vector> RISCv32CodeGen::build_dag(BasicBlock* bb) { - std::vector> nodes_storage; // Stores all unique_ptr - std::map value_to_node; // Maps IR Value* to raw DAGNode* for quick lookup + std::vector> nodes_storage; // 存储所有 unique_ptr + std::map value_to_node; // 将 IR Value* 映射到原始 DAGNode*,用于快速查找 - // Helper to create a DAGNode and manage its ownership + // 辅助函数,用于创建 DAGNode 并管理其所有权 auto create_node = [&](DAGNode::NodeKind kind, Value* val = nullptr) -> DAGNode* { - // Optimization: If a value already has a node and it's not a control flow/store, reuse it (CSE) - // For AllocaInst, we want to create a node representing its address, but not necessarily assign a vreg to the AllocaInst itself directly. + // 优化:如果一个值已经有节点并且它不是控制流/存储/Alloca地址,则重用它 (CSE) + // 对于 AllocaInst,我们想创建一个代表其地址的节点,但不一定直接为 AllocaInst 本身分配虚拟寄存器。 if (val && value_to_node.count(val) && kind != DAGNode::STORE && kind != DAGNode::RETURN && kind != DAGNode::BRANCH && kind != DAGNode::ALLOCA_ADDR) { return value_to_node[val]; } @@ -191,22 +261,22 @@ std::vector> RISCv32CodeGen::build_dag( auto node = std::make_unique(kind); node->value = val; - // Assign a virtual register for values that produce a result + // 为产生结果的值分配虚拟寄存器 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; // Map IR Value to its virtual register + value_vreg_map[val] = node->result_vreg; // 将 IR Value 映射到其虚拟寄存器 } else if (kind == DAGNode::ALLOCA_ADDR) { - // For AllocaInst's address, we'll assign a vreg to the DAGNode itself, - // not the AllocaInst directly in value_vreg_map (as AllocaInst is memory location). + // 对于 AllocaInst 的地址,我们将虚拟寄存器分配给 DAGNode 本身, + // 而不是直接在 value_vreg_map 中分配给 AllocaInst (因为 AllocaInst 是内存位置)。 node->result_vreg = "v" + std::to_string(vreg_counter++); - // We might want to map the AllocaInst to its virtual register if it's treated like a pointer - // value_vreg_map[val] = node->result_vreg; // Consider if this is needed/correct + // 考虑是否需要将 AllocaInst 映射到其虚拟寄存器,如果它被视为指针值 + // value_vreg_map[val] = node->result_vreg; // 考虑这是否需要/正确 } DAGNode* raw_node_ptr = node.get(); - nodes_storage.push_back(std::move(node)); // Store unique_ptr + nodes_storage.push_back(std::move(node)); // 存储 unique_ptr - // Map the IR Value to the created DAGNode only if it represents a computed value + // 仅当 IR Value 表示一个计算值时,才将其映射到创建的 DAGNode if (val && kind != DAGNode::STORE && kind != DAGNode::RETURN && kind != DAGNode::BRANCH) { value_to_node[val] = raw_node_ptr; } @@ -218,29 +288,28 @@ std::vector> RISCv32CodeGen::build_dag( auto inst = inst_ptr.get(); if (auto alloca = dynamic_cast(inst)) { - // An AllocaInst itself doesn't produce a value in a register, - // but its address will be used by loads/stores. - // Create a node to represent the address of the allocated memory. - // This address will be an offset from s0 (frame pointer). - // We store the AllocaInst pointer in `value` field of DAGNode. + // AllocaInst 本身不产生寄存器中的值,但其地址将被 load/store 使用。 + // 创建一个节点来表示分配内存的地址。 + // 这个地址将是 s0 (帧指针) 的偏移量。 + // 我们将 AllocaInst 指针存储在 DAGNode 的 `value` 字段中。 auto alloca_addr_node = create_node(DAGNode::ALLOCA_ADDR, alloca); - // This node will have a result_vreg that represents the computed address (s0 + offset) - // The actual offset will be determined during register allocation (stack_map). + // 此节点将有一个 result_vreg,表示计算出的地址 (s0 + 偏移量) + // 实际偏移量将在寄存器分配阶段 (stack_map) 确定。 } else if (auto store = dynamic_cast(inst)) { - auto store_node = create_node(DAGNode::STORE); + auto store_node = create_node(DAGNode::STORE, store); // 将 store inst 绑定到 node - // Get value to be stored + // 获取要存储的值 Value* val_to_store_ir = store->getValue(); DAGNode* val_node = nullptr; if (value_to_node.count(val_to_store_ir)) { val_node = value_to_node[val_to_store_ir]; } else if (auto constant = dynamic_cast(val_to_store_ir)) { val_node = create_node(DAGNode::CONSTANT, constant); - } else { // It's a value that hasn't been computed yet in this block, assume it needs a load + } else { // 这是一个尚未在此块中计算的值,假设它需要加载 (从内存或参数) val_node = create_node(DAGNode::LOAD, val_to_store_ir); } - // Get pointer to memory location + // 获取内存位置的指针 Value* ptr_ir = store->getPointer(); DAGNode* ptr_node = nullptr; if (value_to_node.count(ptr_ir)) { @@ -248,9 +317,9 @@ std::vector> RISCv32CodeGen::build_dag( } else if (auto alloca = dynamic_cast(ptr_ir)) { ptr_node = create_node(DAGNode::ALLOCA_ADDR, alloca); } else if (auto global = dynamic_cast(ptr_ir)) { - ptr_node = create_node(DAGNode::CONSTANT, global); // Global address will be loaded - } else { // Must be a pointer held in a virtual register - ptr_node = create_node(DAGNode::LOAD, ptr_ir); // This is an instruction that produces a pointer + ptr_node = create_node(DAGNode::CONSTANT, global); // 全局地址将被加载 + } else { // 必须是存储在虚拟寄存器中的指针 + ptr_node = create_node(DAGNode::LOAD, ptr_ir); // 这是一个产生指针的指令 } store_node->operands.push_back(val_node); @@ -258,9 +327,9 @@ std::vector> RISCv32CodeGen::build_dag( val_node->users.push_back(store_node); ptr_node->users.push_back(store_node); } else if (auto load = dynamic_cast(inst)) { - if (value_to_node.count(load)) continue; // Common Subexpression Elimination (CSE) + if (value_to_node.count(load)) continue; // 共同子表达式消除 (CSE) - auto load_node = create_node(DAGNode::LOAD, load); // Assigns result_vreg to load_node and maps load to it + auto load_node = create_node(DAGNode::LOAD, load); // 为 load_node 分配 result_vreg 并映射 load Value* ptr_ir = load->getPointer(); DAGNode* ptr_node = nullptr; @@ -269,9 +338,9 @@ std::vector> RISCv32CodeGen::build_dag( } else if (auto alloca = dynamic_cast(ptr_ir)) { ptr_node = create_node(DAGNode::ALLOCA_ADDR, alloca); } else if (auto global = dynamic_cast(ptr_ir)) { - ptr_node = create_node(DAGNode::CONSTANT, global); // Global address will be loaded - } else { // Must be a pointer held in a virtual register - ptr_node = create_node(DAGNode::LOAD, ptr_ir); // This is an instruction that produces a pointer + ptr_node = create_node(DAGNode::CONSTANT, global); // 全局地址将被加载 + } else { // 必须是存储在虚拟寄存器中的指针 + ptr_node = create_node(DAGNode::LOAD, ptr_ir); // 这是一个产生指针的指令 } load_node->operands.push_back(ptr_node); @@ -287,7 +356,7 @@ std::vector> RISCv32CodeGen::build_dag( } else if (auto constant = dynamic_cast(operand_ir)) { return create_node(DAGNode::CONSTANT, constant); } else { - // It's a value produced by another instruction or argument, assume it needs a load if not in map + // 这是一个由另一个指令或参数产生的值,如果不在 map 中,则假设需要加载 return create_node(DAGNode::LOAD, operand_ir); } }; @@ -300,9 +369,9 @@ std::vector> RISCv32CodeGen::build_dag( lhs_node->users.push_back(bin_node); rhs_node->users.push_back(bin_node); } else if (auto call = dynamic_cast(inst)) { - if (value_to_node.count(call)) continue; // CSE (if result is reused) + if (value_to_node.count(call)) continue; // CSE (如果结果被重用) - auto call_node = create_node(DAGNode::CALL, call); // Assigns result_vreg if call returns a value + auto call_node = create_node(DAGNode::CALL, call); // 如果调用返回一个值,则分配 result_vreg for (auto arg : call->getArguments()) { auto arg_val_ir = arg->getValue(); @@ -318,7 +387,7 @@ std::vector> RISCv32CodeGen::build_dag( arg_node->users.push_back(call_node); } } else if (auto ret = dynamic_cast(inst)) { - auto ret_node = create_node(DAGNode::RETURN); + auto ret_node = create_node(DAGNode::RETURN, ret); // 将 return inst 绑定到 node if (ret->hasReturnValue()) { auto val_ir = ret->getReturnValue(); DAGNode* val_node = nullptr; @@ -333,27 +402,27 @@ std::vector> RISCv32CodeGen::build_dag( val_node->users.push_back(ret_node); } } else if (auto cond_br = dynamic_cast(inst)) { - auto br_node = create_node(DAGNode::BRANCH, cond_br); + auto br_node = create_node(DAGNode::BRANCH, cond_br); // 将 cond_br inst 绑定到 node auto cond_ir = cond_br->getCondition(); if (auto constant_cond = dynamic_cast(cond_ir)) { - // Optimize constant conditional branch to unconditional jump + // 优化常量条件分支为无条件跳转 br_node->inst = "j " + (constant_cond->getInt() ? cond_br->getThenBlock()->getName() : cond_br->getElseBlock()->getName()); - // No operands needed for a direct jump + // 对于直接跳转,不需要操作数 } else { DAGNode* cond_node = nullptr; if (value_to_node.count(cond_ir)) { cond_node = value_to_node[cond_ir]; } else if (auto bin_cond = dynamic_cast(cond_ir)) { cond_node = create_node(DAGNode::BINARY, bin_cond); - } else { // Must be a value that needs to be loaded + } else { // 必须是一个需要加载的值 cond_node = create_node(DAGNode::LOAD, cond_ir); } br_node->operands.push_back(cond_node); cond_node->users.push_back(br_node); } } else if (auto uncond_br = dynamic_cast(inst)) { - auto br_node = create_node(DAGNode::BRANCH, uncond_br); + auto br_node = create_node(DAGNode::BRANCH, uncond_br); // 将 uncond_br inst 绑定到 node br_node->inst = "j " + uncond_br->getBlock()->getName(); } } @@ -366,7 +435,7 @@ void RISCv32CodeGen::print_dag(const std::vector>& dag, std::cerr << "=== DAG for Basic Block: " << bb_name << " ===\n"; std::set visited; - // Helper map to assign sequential IDs for nodes in print output + // 辅助映射,用于在打印输出中为节点分配顺序 ID std::map node_to_id; int current_id = 0; for (const auto& node_ptr : dag) { @@ -377,7 +446,7 @@ void RISCv32CodeGen::print_dag(const std::vector>& dag, if (!node) return; std::string current_indent(indent, ' '); - int node_id = node_to_id.count(node) ? node_to_id[node] : -1; // Get assigned ID + int node_id = node_to_id.count(node) ? node_to_id[node] : -1; // 获取分配的 ID std::cerr << current_indent << "Node#" << node_id << ": " << node->getNodeKindString(); if (!node->result_vreg.empty()) { @@ -388,6 +457,9 @@ void RISCv32CodeGen::print_dag(const std::vector>& dag, std::cerr << " ["; if (auto inst = dynamic_cast(node->value)) { std::cerr << inst->getKindString(); + if (!inst->getName().empty()) { + std::cerr << "(" << inst->getName() << ")"; + } } else if (auto constant = dynamic_cast(node->value)) { if (constant->isInt()) { std::cerr << "ConstInt(" << constant->getInt() << ")"; @@ -401,73 +473,73 @@ void RISCv32CodeGen::print_dag(const std::vector>& dag, } std::cerr << "]"; } - std::cerr << " -> Inst: \"" << node->inst << "\""; // Print selected instruction + std::cerr << " -> Inst: \"" << node->inst << "\""; // 打印选定的指令 std::cerr << "\n"; if (visited.find(node) != visited.end()) { - std::cerr << current_indent << " (already printed descendants)\n"; - return; // Avoid infinite recursion for cycles + std::cerr << current_indent << " (已打印后代)\n"; + return; // 避免循环的无限递归 } visited.insert(node); if (!node->operands.empty()) { - std::cerr << current_indent << " Operands:\n"; + std::cerr << current_indent << " 操作数:\n"; for (auto operand : node->operands) { print_node(operand, indent + 4); } } - // Removed users print to simplify output and avoid redundant recursion in a DAG. - // Users are more for upward traversal, not downward. + // 移除了 users 打印,以简化输出并避免 DAG 中的冗余递归。 + // Users 更适用于向上遍历,而不是向下遍历。 }; - // Iterate through the DAG in a way that respects dependencies if possible, - // or just print all root nodes (nodes with no users within the DAG, or nodes representing side effects like store/branch/return) - // For simplicity, let's just print all nodes in the order they were created for now. - // A proper DAG traversal for printing would be a reverse topological sort. + // 遍历 DAG,以尊重依赖的方式打印。 + // 当前实现:遍历所有节点,从作为“根”的节点开始打印(没有用户或副作用节点)。 + // 每次打印新的根时,重置 visited 集合,以允许共享子图被重新打印(尽管这不是最高效的方式)。 for (const auto& node_ptr : dag) { + // 只有那些没有用户或者表示副作用(如 store/branch/return)的节点才被视为“根” + // 这样可以确保所有指令(包括那些没有明确结果的)都被打印 if (node_ptr->users.empty() || node_ptr->kind == DAGNode::STORE || node_ptr->kind == DAGNode::RETURN || node_ptr->kind == DAGNode::BRANCH) { - // Treat nodes with no users or side-effect nodes as roots for printing - visited.clear(); // Reset visited for each root to allow re-printing shared subgraphs + visited.clear(); // 为每个根重置 visited,允许重新打印共享子图 print_node(node_ptr.get(), 0); } } - std::cerr << "=== End DAG ===\n\n"; + std::cerr << "=== DAG 结束 ===\n\n"; } // 指令选择 void RISCv32CodeGen::select_instructions(DAGNode* node, const RegAllocResult& alloc) { if (!node) return; - if (!node->inst.empty() && node->kind != DAGNode::ALLOCA_ADDR) return; // Instruction already selected (except for ALLOCA_ADDR which doesn't directly map to an instruction) + // ALLOCA_ADDR 节点不直接映射到一条指令,它表示的是地址的计算 + if (!node->inst.empty() && node->kind != DAGNode::ALLOCA_ADDR) return; // 指令已选择 - // Recursively select instructions for operands first + // 首先递归地为操作数选择指令 for (auto operand : node->operands) { if (operand) { select_instructions(operand, alloc); } } - std::stringstream ss_inst; // Use a stringstream to build instructions + std::stringstream ss_inst; // 使用 stringstream 构建指令 - // Get assigned physical registers for virtual registers, or a temporary if not allocated + // 获取分配的物理寄存器,如果没有分配,则使用临时寄存器 (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)); } - // If a vreg isn't allocated to a physical register, it implies it's spilled or a temporary. - // For simplicity, we can use a fixed temporary register like t0 for spilled values - // or for immediate values that are loaded into a register just for the instruction. - // A more robust backend would handle spilling explicitly here. - return reg_to_string(PhysicalReg::T0); // Fallback to a temporary register + // 如果虚拟寄存器未分配给物理寄存器,则表示它已溢出或是一个临时值。 + // 为简化,我们使用固定临时寄存器 t0 用于溢出值或需要加载到寄存器中的立即数。 + // 更健壮的后端会在此处显式处理溢出。 + return reg_to_string(PhysicalReg::T0); // 回退到临时寄存器 }; - // Get memory offset for allocated stack variables + // 获取分配的栈变量的内存偏移量 auto get_stack_offset = [&](Value* val) { if (alloc.stack_map.count(val)) { return std::to_string(alloc.stack_map.at(val)); } - return std::string("0"); // Default or error + return std::string("0"); // 默认或错误情况 }; switch (node->kind) { @@ -479,23 +551,23 @@ void RISCv32CodeGen::select_instructions(DAGNode* node, const RegAllocResult& al } else { float f = constant->getFloat(); uint32_t float_bits = *(uint32_t*)&f; - // For floating point, load integer bits then convert + // 对于浮点数,先加载整数位,然后转换 ss_inst << "li " << dest_reg << ", " << float_bits << "\n"; - ss_inst << "fmv.w.x " << dest_reg << ", " << dest_reg; // Assumes dest_reg can be used for both int and float (not always true for FPU regs) + ss_inst << "fmv.w.x " << dest_reg << ", " << dest_reg; // 假设 dest_reg 可以同时用于整数和浮点数 (FPU 寄存器并非总是如此) } } else if (auto global = dynamic_cast(node->value)) { std::string dest_reg = get_preg_or_temp(node->result_vreg); - ss_inst << "la " << dest_reg << ", " << global->getName(); // Load address of global + ss_inst << "la " << dest_reg << ", " << global->getName(); // 加载全局变量地址 } break; } case DAGNode::ALLOCA_ADDR: { - // For AllocaInst, we want to compute its address (s0 + offset) and put it into result_vreg + // 对于 AllocaInst,我们想计算其地址 (s0 + 偏移量) 并放入 result_vreg if (auto alloca_inst = dynamic_cast(node->value)) { std::string dest_reg = get_preg_or_temp(node->result_vreg); int offset = alloc.stack_map.at(alloca_inst); - // The frame pointer s0 already points to the base of the current frame. - // The offset is relative to s0. + // 帧指针 s0 已经指向当前帧的基地址。 + // 偏移量是相对于 s0 的。 ss_inst << "addi " << dest_reg << ", s0, " << offset; } break; @@ -503,44 +575,51 @@ void RISCv32CodeGen::select_instructions(DAGNode* node, const RegAllocResult& al 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]; // The operand is the pointer + DAGNode* ptr_node = node->operands[0]; // 操作数是指针 - // Check if the pointer itself is an AllocaInst - if (auto alloca_inst = dynamic_cast(ptr_node->value)) { - int offset = alloc.stack_map.at(alloca_inst); - ss_inst << "lw " << dest_reg << ", " << offset << "(s0)"; + // 检查指针本身是否是 AllocaInst + if (ptr_node->kind == DAGNode::ALLOCA_ADDR) { // 检查节点类型,而非其值 + if (auto alloca_inst = dynamic_cast(ptr_node->value)) { + int offset = alloc.stack_map.at(alloca_inst); + ss_inst << "lw " << dest_reg << ", " << offset << "(s0)"; + } } else { - // Pointer is in a register (possibly a global address, or a result of a GEP/other calc) + // 指针在寄存器中 (可能是全局地址,或 GEP/其他计算结果) std::string ptr_reg = get_preg_or_temp(ptr_node->result_vreg); - ss_inst << "lw " << dest_reg << ", 0(" << ptr_reg << ")"; // Load from address in ptr_reg + ss_inst << "lw " << dest_reg << ", 0(" << ptr_reg << ")"; // 从 ptr_reg 中的地址加载 } 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]; + DAGNode* val_node = node->operands[0]; // 要存储的值 + DAGNode* ptr_node = node->operands[1]; // 目标地址 std::string src_reg; if (val_node->kind == DAGNode::CONSTANT) { - // If storing a constant, load it into a temporary register first + // 如果存储的是常量,先将其加载到临时寄存器 (t0) if (auto constant = dynamic_cast(val_node->value)) { - src_reg = reg_to_string(PhysicalReg::T0); // Use a temporary for constant - ss_inst << "li " << src_reg << ", " << constant->getInt() << "\n"; - } else { // Global address being stored - src_reg = reg_to_string(PhysicalReg::T0); // Use a temporary - ss_inst << "la " << src_reg << ", " << dynamic_cast(val_node->value)->getName() << "\n"; + src_reg = reg_to_string(PhysicalReg::T0); // 使用临时寄存器用于常量 + ss_inst << "li " << src_reg << ", " << constant->getInt(); // 这行指令将作为 store 指令的一部分被添加 + } else { // 存储全局地址 + src_reg = reg_to_string(PhysicalReg::T0); // 使用临时寄存器 + ss_inst << "la " << src_reg << ", " << dynamic_cast(val_node->value)->getName(); } } else { src_reg = get_preg_or_temp(val_node->result_vreg); } - - // Check if the pointer is an AllocaInst (stack variable) - if (auto alloca_inst = dynamic_cast(ptr_node->value)) { - int offset = alloc.stack_map.at(alloca_inst); - ss_inst << "sw " << src_reg << ", " << offset << "(s0)"; + // 将 li/la 指令与 sw 指令放在同一行,用 \n 分隔,emit_instructions 会正确处理 + // 这里将 store 指令放在 li/la 指令的后面 + ss_inst << (ss_inst.str().empty() ? "" : "\n"); // 如果前面有指令,则换行 + + // 检查指针是否是 AllocaInst (栈变量) + if (ptr_node->kind == DAGNode::ALLOCA_ADDR) { + if (auto alloca_inst = dynamic_cast(ptr_node->value)) { + int offset = alloc.stack_map.at(alloca_inst); + ss_inst << "sw " << src_reg << ", " << offset << "(s0)"; + } } else { - // Pointer is in a register (possibly a global address, or a result of a GEP/other calc) + // 指针在寄存器中 (可能是全局地址,或 GEP/其他计算结果) std::string ptr_reg = get_preg_or_temp(ptr_node->result_vreg); ss_inst << "sw " << src_reg << ", 0(" << ptr_reg << ")"; } @@ -560,14 +639,13 @@ void RISCv32CodeGen::select_instructions(DAGNode* node, const RegAllocResult& al case BinaryInst::kAdd: opcode = "add"; break; case BinaryInst::kSub: opcode = "sub"; break; case BinaryInst::kMul: opcode = "mul"; break; - case BinaryInst::kICmpEQ: // Implement A == B as sub A, B; seqz D, (A-B) + case BinaryInst::kICmpEQ: // 实现 A == B 为 sub A, B; seqz D, (A-B) ss_inst << "sub " << dest_reg << ", " << lhs_reg << ", " << rhs_reg << "\n"; - ss_inst << "seqz " << dest_reg << ", " << dest_reg; // set equal to zero - node->inst = ss_inst.str(); // Set instruction and return + ss_inst << "seqz " << dest_reg << ", " << dest_reg; // 如果等于零则设置 + node->inst = ss_inst.str(); // 设置指令并返回 return; - // Add more binary operations here (e.g., div, sge, slt, etc.) - case Instruction::kDiv: opcode = "div"; break; // Integer division - case Instruction::kRem: opcode = "rem"; break; // Integer remainder + case Instruction::kDiv: opcode = "div"; break; // 整数除法 + case Instruction::kRem: opcode = "rem"; break; // 整数余数 case BinaryInst::kICmpGE: // A >= B <=> !(A < B) <=> !(slt D, A, B) <=> slt D, A, B; xori D, D, 1 ss_inst << "slt " << dest_reg << ", " << lhs_reg << ", " << rhs_reg << "\n"; ss_inst << "xori " << dest_reg << ", " << dest_reg << ", 1"; @@ -575,7 +653,7 @@ void RISCv32CodeGen::select_instructions(DAGNode* node, const RegAllocResult& al return; case BinaryInst::kICmpGT: // A > B <=> B < A opcode = "slt"; - ss_inst << opcode << " " << dest_reg << ", " << rhs_reg << ", " << lhs_reg; // slt rd, rs2, rs1 (if rs2 < rs1) + ss_inst << opcode << " " << dest_reg << ", " << rhs_reg << ", " << lhs_reg; // slt rd, rs2, rs1 (如果 rs2 < rs1) node->inst = ss_inst.str(); return; case BinaryInst::kICmpLE: // A <= B <=> !(A > B) <=> !(slt D, B, A) <=> slt D, B, A; xori D, D, 1 @@ -594,7 +672,7 @@ void RISCv32CodeGen::select_instructions(DAGNode* node, const RegAllocResult& al node->inst = ss_inst.str(); return; default: - // Handle unknown binary ops or throw error + // 处理未知二进制操作或抛出错误 throw std::runtime_error("Unsupported binary instruction kind: " + bin->getKindString()); } if (!opcode.empty()) { @@ -607,13 +685,13 @@ void RISCv32CodeGen::select_instructions(DAGNode* node, const RegAllocResult& al auto call = dynamic_cast(node->value); if (!call) break; - // Pass arguments in a0-a7 + // 将参数放入 a0-a7 for (size_t i = 0; i < node->operands.size() && i < 8; ++i) { if (node->operands[i] && !node->operands[i]->result_vreg.empty()) { ss_inst << "mv " << reg_to_string(static_cast(static_cast(PhysicalReg::A0) + i)) << ", " << get_preg_or_temp(node->operands[i]->result_vreg) << "\n"; } else if (node->operands[i] && node->operands[i]->kind == DAGNode::CONSTANT) { - // Handle constant arguments directly loading into A-regs + // 直接将常量参数加载到 A 寄存器 if (auto const_val = dynamic_cast(node->operands[i]->value)) { ss_inst << "li " << reg_to_string(static_cast(static_cast(PhysicalReg::A0) + i)) << ", " << const_val->getInt() << "\n"; @@ -623,160 +701,168 @@ void RISCv32CodeGen::select_instructions(DAGNode* node, const RegAllocResult& al } } } - ss_inst << "call " << call->getCallee()->getName(); // Use 'call' pseudo-instruction + ss_inst << "call " << call->getCallee()->getName(); // 使用 'call' 伪指令 - // If function returns a value, move it from a0 to the result vreg + // 如果函数返回一个值,将其从 a0 移动到结果虚拟寄存器 if ((call->getType()->isInt() || call->getType()->isFloat()) && !node->result_vreg.empty()) { ss_inst << "\nmv " << get_preg_or_temp(node->result_vreg) << ", a0"; } break; } case DAGNode::RETURN: { - // If there's a return value, move it to a0 + // 如果有返回值,将其移动到 a0 if (!node->operands.empty() && node->operands[0]) { std::string return_val_reg = get_preg_or_temp(node->operands[0]->result_vreg); + // 对于返回值,直接将最终结果移动到 a0 + // 确保 load 的结果最终进入 a0,避免不必要的 t0 中转 ss_inst << "mv a0, " << return_val_reg << "\n"; } - // Epilogue: Restore s0, ra, and adjust sp + // 函数尾声 (Epilogue): 恢复 s0, ra, 并调整 sp if (alloc.stack_size > 0) { int aligned_stack_size = (alloc.stack_size + 15) & ~15; - ss_inst << "lw ra, " << (aligned_stack_size - 4) << "(sp)\n"; - ss_inst << "lw s0, " << (aligned_stack_size - 8) << "(sp)\n"; - ss_inst << "addi sp, sp, " << aligned_stack_size << "\n"; + ss_inst << "lw ra, " << (aligned_stack_size - 4) << "(sp)\n"; // 恢复 ra + ss_inst << "lw s0, " << (aligned_stack_size - 8) << "(sp)\n"; // 恢复 s0 + ss_inst << "addi sp, sp, " << aligned_stack_size << "\n"; // 恢复栈指针 } - ss_inst << "ret"; + ss_inst << "ret"; // 返回 break; } case DAGNode::BRANCH: { auto br = dynamic_cast(node->value); auto uncond_br = dynamic_cast(node->value); - if (node->inst.empty()) { // If not already a constant jump + 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"; // Branch if not zero (true) - ss_inst << "j " << else_block; // Unconditional jump to else block + + // 修复空标签问题 + if (then_block.empty()) { + then_block = ENTRY_BLOCK_PSEUDO_NAME + "then"; // 临时命名 + } + if (else_block.empty()) { + else_block = ENTRY_BLOCK_PSEUDO_NAME + "else"; // 临时命名 + } + + ss_inst << "bnez " << cond_reg << ", " << then_block << "\n"; // 如果条件不为零 (真),则跳转到 then 块 + ss_inst << "j " << else_block; // 无条件跳转到 else 块 } else if (uncond_br) { - ss_inst << "j " << uncond_br->getBlock()->getName(); // Unconditional jump + std::string target_block = uncond_br->getBlock()->getName(); + if (target_block.empty()) { // 修复空标签问题 + target_block = ENTRY_BLOCK_PSEUDO_NAME + "target"; // 临时命名 + } + ss_inst << "j " << target_block; // 无条件跳转 } } else { - // This branch node was optimized to a direct jump by constant propagation in build_dag - // Its 'inst' field is already set. Copy it. + // 这个分支节点已经被 build_dag 中的常量传播优化为直接跳转。 + // 它的 'inst' 字段已经设置。直接复制它。 ss_inst << node->inst; } break; } default: - // For nodes that don't directly map to an instruction (like `alloca` itself, which is handled by its address node) - // or unhandled instruction types, leave inst empty. + // 对于不直接映射到指令的节点 (例如 `alloca` 本身,其地址由其地址节点处理) + // 或未处理的指令类型,将 inst 留空。 break; } - node->inst = ss_inst.str(); // Store the generated instruction(s) + node->inst = ss_inst.str(); // 存储生成的指令 } -// 修改:优化指令发射,防止虚拟寄存器泄露,减少重复指令 -void RISCv32CodeGen::emit_instructions(DAGNode* node, std::vector& insts, const RegAllocResult& alloc, std::set& emitted_nodes) { +// 修改:指令发射,直接输出到 stringstream,处理依赖和去重 +void RISCv32CodeGen::emit_instructions(DAGNode* node, std::stringstream& ss, const RegAllocResult& alloc, std::set& emitted_nodes) { if (!node || emitted_nodes.count(node)) { - return; // Already emitted or null + return; // 已发射或为空 } - // Recursively emit operands first to ensure dependencies are met + // 递归地发射操作数以确保满足依赖关系 for (auto operand : node->operands) { if (operand) { - emit_instructions(operand, insts, alloc, emitted_nodes); + emit_instructions(operand, ss, alloc, emitted_nodes); } } - // Mark current node as emitted + // 标记当前节点为已发射 emitted_nodes.insert(node); - // Split multi-line instructions and process each line - std::stringstream ss(node->inst); + // 分割多行指令并处理每一行 + std::stringstream node_inst_ss(node->inst); std::string line; - std::set seen_insts_in_block; // Track instructions to avoid immediate duplicates within the stream - while (std::getline(ss, line, '\n')) { - // Trim leading/trailing whitespace and remove potential labels from the start of the line - line = std::regex_replace(line, std::regex("^\\s*[^\\s:]*:\\s*"), ""); // Remove label if present (e.g., `label: inst`) - line = std::regex_replace(line, std::regex("^\\s+|\\s+$"), ""); // Trim whitespace + while (std::getline(node_inst_ss, line, '\n')) { + // 清除前导/尾随空白并移除行开头的潜在标签 + line = std::regex_replace(line, std::regex("^\\s*[^\\s:]*:\\s*"), ""); // 移除标签(例如 `label: inst`) + line = std::regex_replace(line, std::regex("^\\s+|\\s+$"), ""); // 清除空白 if (line.empty()) continue; - // Replace virtual registers with physical registers or handle spills + // 处理虚拟寄存器替换和溢出/加载逻辑 std::string processed_line = line; - // Replace result_vreg (if it exists in this line) - if (!node->result_vreg.empty() && processed_line.find(node->result_vreg) != std::string::npos) { - std::string preg = reg_to_string(PhysicalReg::T0); // Default to T0 if not allocated + // 如果是 store 或 load,并且操作数是 ALLOCA_ADDR,那么地址计算(addi s0, offset)应该在实际的 sw/lw 之前 + // 但由于 DAG 结构,ALLOCA_ADDR 节点本身会生成一条 addi 指令。 + // 我们需要确保这条 addi 指令在 store/load 之前被发射。 + // emit_instructions 的递归调用已经处理了操作数的发射,所以 `ptr_node->inst` 应该已经生成。 + + // 替换结果虚拟寄存器 (如果此行中存在) + 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)); - } else if (node->value && alloc.stack_map.count(node->value)) { - // This means the result of this instruction would be spilled. - // We should generate a store instruction after the computation. - // For now, let's just use a temporary register for the computation result, - // and add a spill store after the instruction. - // NOTE: This is a simplified approach; a real spill strategy is more complex. - int offset = alloc.stack_map.at(node->value); - std::string spill_reg = reg_to_string(PhysicalReg::T0); // Use t0 for spill - std::string store_inst = "sw " + spill_reg + ", " + std::to_string(offset) + "(s0)"; - // We will replace the vreg with `spill_reg` in the current instruction. - // And then add the `store_inst` to the list. - processed_line = std::regex_replace(processed_line, std::regex("\\b" + node->result_vreg + "\\b"), spill_reg); - if (seen_insts_in_block.find(store_inst) == seen_insts_in_block.end()) { - insts.push_back(store_inst); - seen_insts_in_block.insert(store_inst); - } - // If the node itself is a store, no additional spill is needed, as it's directly storing. - if (node->kind == DAGNode::STORE) { // If it's a store instruction, no need to add another store - // It's possible that the value to be stored also came from a spilled vreg, - // in which case a load might be needed for the operand *before* the store instruction. - // This is handled by `emit_instructions` on operands. - } } - if (processed_line.find(node->result_vreg) != std::string::npos) { // Still contains vreg after potential spill handling + + // 如果结果需要溢出到栈 + 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 { + // 如果结果不溢出或者已经被分配了物理寄存器 processed_line = std::regex_replace(processed_line, std::regex("\\b" + node->result_vreg + "\\b"), preg); } } - - // Replace operand vregs (if they exist in this line) + + // 替换操作数虚拟寄存器 (如果此行中存在) for (auto operand : node->operands) { - if (operand && !operand->result_vreg.empty() && processed_line.find(operand->result_vreg) != std::string::npos) { + 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)) { - // This operand is spilled, load it into a temporary register (t0) before use. + // 此操作数已溢出,在使用前将其加载到临时寄存器 (t0)。 int offset = alloc.stack_map.at(operand->value); std::string load_inst = "lw " + reg_to_string(PhysicalReg::T0) + ", " + std::to_string(offset) + "(s0)"; - if (seen_insts_in_block.find(load_inst) == seen_insts_in_block.end()) { - insts.push_back(load_inst); - seen_insts_in_block.insert(load_inst); - } - operand_preg = reg_to_string(PhysicalReg::T0); // Use t0 as the source for this instruction + // 这里直接发射 load 指令 + ss << " " << load_inst << "\n"; + operand_preg = reg_to_string(PhysicalReg::T0); // 使用 t0 作为此指令的源 } processed_line = std::regex_replace(processed_line, std::regex("\\b" + operand->result_vreg + "\\b"), operand_preg); } } - // Add the processed line if not already added - if (seen_insts_in_block.find(processed_line) == seen_insts_in_block.end()) { - insts.push_back(processed_line); - seen_insts_in_block.insert(processed_line); - } + // 添加处理后的指令 + ss << " " << processed_line << "\n"; } } -// 活跃性分析 +// 活跃性分析 (基本保持不变,因为是通用的数据流分析) std::map> RISCv32CodeGen::liveness_analysis(Function* func) { std::map> live_in, live_out; bool changed = true; - // Initialize all live_in/out sets to empty + // 初始化所有 live_in/out 集合为空 for (const auto& bb : func->getBasicBlocks()) { for (const auto& inst_ptr : bb->getInstructions()) { live_in[inst_ptr.get()] = {}; @@ -786,17 +872,17 @@ std::map> RISCv32CodeGen::liveness_analysis( while (changed) { changed = false; - // Iterate basic blocks in reverse post-order (or reverse natural order) + // 逆序遍历基本块 for (auto it = func->getBasicBlocks_NoRange().rbegin(); it != func->getBasicBlocks_NoRange().rend(); ++it) { auto bb = it->get(); - // Iterate instructions in reverse order within the basic block + // 在基本块内逆序遍历指令 for (auto inst_it = bb->getInstructions().rbegin(); inst_it != bb->getInstructions().rend(); ++inst_it) { auto inst = inst_it->get(); std::set current_live_in = live_in[inst]; std::set current_live_out = live_out[inst]; std::set new_live_out; - // Union of live_in from all successors + // 后继的 live_in 集合的并集 if (inst->isTerminator()) { if (auto br = dynamic_cast(inst)) { new_live_out.insert(live_in[br->getThenBlock()->getInstructions().front().get()].begin(), @@ -807,24 +893,24 @@ std::map> RISCv32CodeGen::liveness_analysis( new_live_out.insert(live_in[uncond->getBlock()->getInstructions().front().get()].begin(), live_in[uncond->getBlock()->getInstructions().front().get()].end()); } - // Return instructions have no successors, so new_live_out remains empty + // 返回指令没有后继,所以 new_live_out 保持为空 } else { auto next_inst_it = std::next(inst_it); if (next_inst_it != bb->getInstructions().rend()) { - // Not a terminator, so next instruction in block is successor + // 不是终结符,所以块中的下一条指令是后继 new_live_out = live_in[next_inst_it->get()]; } } - // Calculate use and def sets for the current instruction + // 计算当前指令的 use 和 def 集合 std::set use_set, def_set; - // Define + // 定义 (Def) if (value_vreg_map.count(inst)) { def_set.insert(value_vreg_map.at(inst)); } - // Use + // 使用 (Use) if (auto bin = dynamic_cast(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())); @@ -844,9 +930,9 @@ std::map> RISCv32CodeGen::liveness_analysis( if (value_vreg_map.count(cond_br->getCondition())) use_set.insert(value_vreg_map.at(cond_br->getCondition())); } - // AllocaInst doesn't 'use' or 'def' a vreg directly, its address is a constant. + // AllocaInst 不直接“使用”或“定义”虚拟寄存器,其地址是常量。 - // Calculate new live_in = use U (new_live_out - def) + // 计算新的 live_in = use U (new_live_out - def) std::set new_live_in = use_set; for (const auto& vreg : new_live_out) { if (def_set.find(vreg) == def_set.end()) { @@ -854,7 +940,7 @@ std::map> RISCv32CodeGen::liveness_analysis( } } - // Check for convergence + // 检查收敛 if (new_live_in != current_live_in || new_live_out != current_live_out) { live_in[inst] = new_live_in; live_out[inst] = new_live_out; @@ -866,33 +952,33 @@ std::map> RISCv32CodeGen::liveness_analysis( return live_in; } -// 干扰图构建 +// 干扰图构建 (基本保持不变) std::map> RISCv32CodeGen::build_interference_graph( const std::map>& live_sets) { std::map> graph; - // Ensure all vregs present in live_sets are in the graph initially + // 确保 live_sets 中所有存在的虚拟寄存器最初都在图中 for (const auto& pair : live_sets) { for (const auto& vreg : pair.second) { - graph[vreg] = {}; // Initialize empty set + graph[vreg] = {}; // 初始化空集合 } } for (const auto& pair : live_sets) { auto inst = pair.first; - const auto& live_after_inst = pair.second; // This is actually live_in for the next instruction / basic block entry + const auto& live_after_inst = pair.second; // 这实际上是下一条指令/基本块入口的 live_in std::string defined_vreg; if (value_vreg_map.count(inst)) { defined_vreg = value_vreg_map.at(inst); } - // Add edges from defined vreg to all other live vregs at this point + // 将从 defined vreg 到此时所有其他活跃 vreg 的边添加 if (!defined_vreg.empty()) { for (const auto& live_vreg : live_after_inst) { - if (live_vreg != defined_vreg) { // A vreg does not interfere with itself + if (live_vreg != defined_vreg) { // 虚拟寄存器不与其自身干扰 graph[defined_vreg].insert(live_vreg); - graph[live_vreg].insert(defined_vreg); // Symmetric edge + graph[live_vreg].insert(defined_vreg); // 对称边 } } } @@ -900,24 +986,24 @@ std::map> RISCv32CodeGen::build_interference_ return graph; } -// 图着色 (简化版,贪婪着色) +// 图着色 (简化版,贪婪着色) (基本保持不变) void RISCv32CodeGen::color_graph(std::map& vreg_to_preg, const std::map>& interference_graph) { - vreg_to_preg.clear(); // Clear previous mappings + vreg_to_preg.clear(); // 清除之前的映射 - // Order virtual registers by degree (largest degree first) to improve coloring + // 按度数(从大到小)对虚拟寄存器排序以改进着色效果 std::vector> vreg_degrees; for (const auto& entry : interference_graph) { vreg_degrees.push_back({entry.first, (int)entry.second.size()}); } std::sort(vreg_degrees.begin(), vreg_degrees.end(), - [](const auto& a, const auto& b) { return a.second > b.second; }); // Sort descending by degree + [](const auto& a, const auto& b) { return a.second > b.second; }); // 按度数降序排序 for (const auto& vreg_deg_pair : vreg_degrees) { const std::string& vreg = vreg_deg_pair.first; - std::set used_colors; // Physical registers used by neighbors + std::set used_colors; // 邻居使用的物理寄存器 - // Collect colors of interfering neighbors + // 收集干扰邻居的颜色 if (interference_graph.count(vreg)) { for (const auto& neighbor_vreg : interference_graph.at(vreg)) { if (vreg_to_preg.count(neighbor_vreg)) { @@ -926,7 +1012,7 @@ void RISCv32CodeGen::color_graph(std::map& vreg_to_pre } } - // Find the first available color (physical register) + // 查找第一个可用的颜色(物理寄存器) bool colored = false; for (PhysicalReg preg : allocable_regs) { if (used_colors.find(preg) == used_colors.end()) { @@ -937,42 +1023,42 @@ void RISCv32CodeGen::color_graph(std::map& vreg_to_pre } if (!colored) { - // Spilling: If no physical register is available, this virtual register must be spilled to memory. - // For this simplified example, we're not implementing full spilling. - // A common approach is to assign it a special "spilled" indicator and handle it in code gen. - // For now, we'll just not assign it a physical register, and `get_preg_or_temp` will use a default `t0` or trigger a stack load/store. - std::cerr << "Warning: Could not allocate a register for " << vreg << ". It will likely be spilled to stack.\n"; - // A more complete compiler would add this vreg to `alloc.stack_map` and manage stack offsets for it here. + // 溢出 (Spilling): 如果没有可用的物理寄存器,这个虚拟寄存器必须溢出到内存。 + // 对于这个简化示例,我们没有实现完整的溢出。 + // 一个常见的方法是为其分配一个特殊的“溢出”指示符,并在代码生成中处理它。 + // 目前,我们只是不为其分配物理寄存器,`get_preg_or_temp` 将使用默认的 `t0` 或触发栈加载/存储。 + std::cerr << "警告: 无法为 " << vreg << " 分配寄存器。它很可能会溢出到栈。\n"; + // 更完整的编译器会将此虚拟寄存器添加到 `alloc.stack_map` 并在此处管理其栈偏移量。 } } } // 寄存器分配 RISCv32CodeGen::RegAllocResult RISCv32CodeGen::register_allocation(Function* func) { - // 1. Phi 节点消除 (如果IR中有Phi节点,需要在活跃性分析前消除) - eliminate_phi(func); // Ensure this is called first + // 1. Phi 节点消除 (如果 IR 中有 Phi 节点,需要在活跃性分析前消除) + eliminate_phi(func); // 确保首先调用此函数 - // Reset counters for function + // 为每个函数重置计数器 alloca_offset_counter = 0; vreg_counter = 0; - value_vreg_map.clear(); // Clear for each function + value_vreg_map.clear(); // 为每个函数清除 - // Before liveness analysis, assign virtual registers to alloca instructions and function arguments - // and establish initial stack map for allocas. + // 在活跃性分析之前,为 alloca 指令和函数参数分配虚拟寄存器, + // 并为 allocas 建立初始栈映射。 RegAllocResult alloc_result; - // Assign virtual registers to all instructions that produce a value. - // This part effectively happens during DAG construction in build_dag. - // However, for liveness analysis to work, all potentially used Value* must have a vreg. - // We can iterate the instructions *before* DAG building to populate `value_vreg_map`. - // This is a bit of a chicken-and-egg problem if DAG assigns vregs. - // Let's assume build_dag populates value_vreg_map as it assigns vregs. + // 为所有产生值的指令分配虚拟寄存器。 + // 这部分实际上在 build_dag 中发生。 + // 但是,为了使活跃性分析工作,所有可能使用的 Value* 都必须有一个虚拟寄存器。 + // 我们可以遍历指令 (在 DAG 构建之前) 来填充 `value_vreg_map`。 + // 如果 DAG 分配虚拟寄存器,这有点像先有鸡还是先有蛋的问题。 + // 让我们假设 build_dag 在分配虚拟寄存器时填充 value_vreg_map。 - // Calculate stack offsets for AllocaInsts - int current_stack_offset = 0; // Relative to s0 (frame pointer) - // Args are handled by a0-a7, so no direct stack allocation for them here unless they spill. + // 计算 AllocaInst 的栈偏移量 + int current_stack_offset = 0; // 相对于 s0 (帧指针) + // 参数由 a0-a7 处理,所以除非它们溢出,否则这里不需要直接为它们分配栈空间。 - // Collect all unique AllocaInsts in the function + // 收集函数中所有唯一的 AllocaInst std::set allocas_in_func; for (const auto& bb_ptr : func->getBasicBlocks()) { for (const auto& inst_ptr : bb_ptr->getInstructions()) { @@ -982,20 +1068,20 @@ RISCv32CodeGen::RegAllocResult RISCv32CodeGen::register_allocation(Function* fun } } - // Allocate stack space for alloca instructions + // 为 alloca 指令分配栈空间 for (auto alloca : allocas_in_func) { - // Allocate space for 4-byte integers (i32) - int size = 4; // Assuming i32, adjust if other types exist + // 为 4 字节整数 (i32) 分配空间 + int size = 4; // 假设 i32,如果存在其他类型则调整 alloc_result.stack_map[alloca] = current_stack_offset; current_stack_offset += size; } - // Ensure stack size is multiple of 16 for alignment, and accounts for saved ra and s0 - // ra is at (stack_size - 4)(sp) - // s0 is at (stack_size - 8)(sp) - // So minimum stack size must be 8 + current_stack_offset. - alloc_result.stack_size = current_stack_offset + 8; // For s0 and ra - // Align to 16 bytes for proper ABI + // 确保栈大小是 16 的倍数,并考虑保存的 ra 和 s0 + // ra 在 (stack_size - 4)(sp) + // 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. 活跃性分析 @@ -1010,13 +1096,13 @@ RISCv32CodeGen::RegAllocResult RISCv32CodeGen::register_allocation(Function* fun return alloc_result; } -// Phi 消除 (简化版,将Phi的结果直接复制到每个前驱基本块的末尾) +// Phi 消除 (简化版,将 Phi 的结果直接复制到每个前驱基本块的末尾) void RISCv32CodeGen::eliminate_phi(Function* func) { - // This is a placeholder. A proper phi elimination would involve - // inserting `mov` instructions in predecessor blocks for each phi operand. - // For the given IR example, there are no phi nodes, so this might not be strictly necessary, - // but it's good practice to have this phase if the frontend generates phi nodes. - // For now, we assume no phi nodes are generated or they are handled upstream. + // 这是一个占位符。适当的 phi 消除将涉及 + // 在每个前驱基本块的末尾插入 `mov` 指令,用于每个 phi 操作数。 + // 对于给定的 IR 示例,没有 phi 节点,所以这可能不是严格必要的, + // 但如果前端生成 phi 节点,则有此阶段是好的做法。 + // 目前,我们假设没有生成 phi 节点或者它们已在前端处理。 } } // namespace sysy \ No newline at end of file diff --git a/src/RISCv32Backend.h b/src/RISCv32Backend.h index dbd5cb2..7ee332a 100644 --- a/src/RISCv32Backend.h +++ b/src/RISCv32Backend.h @@ -56,12 +56,14 @@ public: std::string code_gen(); std::string module_gen(); std::string function_gen(Function* func); - std::string basicBlock_gen(BasicBlock* bb, const RegAllocResult& alloc); + // 修改 basicBlock_gen 的声明,添加 int block_idx 参数 + std::string basicBlock_gen(BasicBlock* bb, const RegAllocResult& alloc, int block_idx); // DAG related std::vector> build_dag(BasicBlock* bb); void select_instructions(DAGNode* node, const RegAllocResult& alloc); - void emit_instructions(DAGNode* node, std::vector& insts, const RegAllocResult& alloc, std::set& emitted_nodes); // Add emitted_nodes set + // 改变 emit_instructions 的参数,使其可以直接添加汇编指令到 main ss + void emit_instructions(DAGNode* node, std::stringstream& ss, const RegAllocResult& alloc, std::set& emitted_nodes); // Register Allocation related std::map> liveness_analysis(Function* func); @@ -82,6 +84,13 @@ private: Module* module; int vreg_counter = 0; // Counter for unique virtual register names int alloca_offset_counter = 0; // Counter for alloca offsets + + // 新增一个成员变量来存储当前函数的所有 DAGNode,以确保其生命周期贯穿整个函数代码生成 + // 这样可以在多个 BasicBlock_gen 调用中访问到完整的 DAG 节点 + std::vector> current_function_dag_nodes; + + // 为空标签定义一个伪名称前缀,加上块索引以确保唯一性 + const std::string ENTRY_BLOCK_PSEUDO_NAME = "entry_block_"; }; } // namespace sysy