diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 01f54d8..007966f 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -34,6 +34,7 @@ add_executable(sysyc RISCv64RegAlloc.cpp RISCv64AsmPrinter.cpp RISCv64Passes.cpp + RISCv64LLIR.cpp ) # 设置 include 路径,包含 ANTLR 运行时库和项目头文件 diff --git a/src/RISCv64LLIR.cpp b/src/RISCv64LLIR.cpp new file mode 100644 index 0000000..3816ad1 --- /dev/null +++ b/src/RISCv64LLIR.cpp @@ -0,0 +1,6 @@ +#include "RISCv64LLIR.h" +#include + +namespace sysy { + +} \ No newline at end of file diff --git a/src/RISCv64RegAlloc.cpp b/src/RISCv64RegAlloc.cpp index 97f9614..279fea9 100644 --- a/src/RISCv64RegAlloc.cpp +++ b/src/RISCv64RegAlloc.cpp @@ -2,6 +2,8 @@ #include "RISCv64ISel.h" #include #include +#include // For DEBUG output +#include // For assert namespace sysy { @@ -15,15 +17,28 @@ RISCv64RegAlloc::RISCv64RegAlloc(MachineFunction* mfunc) : MFunc(mfunc) { PhysicalReg::S4, PhysicalReg::S5, PhysicalReg::S6, PhysicalReg::S7, PhysicalReg::S8, PhysicalReg::S9, PhysicalReg::S10, PhysicalReg::S11, }; + + // 映射物理寄存器到特殊的虚拟寄存器ID,用于干扰图中的物理寄存器节点 + // 确保这些特殊ID不会与vreg_counter生成的常规虚拟寄存器ID冲突 + for (PhysicalReg preg : allocable_int_regs) { + preg_to_vreg_id_map[preg] = static_cast(PhysicalReg::PHYS_REG_START_ID) + static_cast(preg); + } } +// 寄存器分配的主入口点 void RISCv64RegAlloc::run() { - handleCallingConvention(); - eliminateFrameIndices(); - analyzeLiveness(); - buildInterferenceGraph(); - colorGraph(); - rewriteFunction(); + // 阶段 1: 处理函数调用约定(参数寄存器预着色) + handleCallingConvention(); + // 阶段 2: 消除帧索引(为局部变量和栈参数分配栈偏移) + eliminateFrameIndices(); + // 阶段 3: 活跃性分析 + analyzeLiveness(); + // 阶段 4: 构建干扰图(包含CALL指令对调用者保存寄存器的影响) + buildInterferenceGraph(); + // 阶段 5: 图着色算法分配物理寄存器 + colorGraph(); + // 阶段 6: 重写函数(插入溢出/填充代码,替换虚拟寄存器为物理寄存器) + rewriteFunction(); } /** @@ -43,7 +58,10 @@ void RISCv64RegAlloc::handleCallingConvention() { if (arg_idx >= 8) { break; } - + // 获取该 Argument 对象对应的虚拟寄存器ID + // 通过 MachineFunction -> RISCv64ISel -> vreg_map 来获取 + const auto& vreg_map_from_isel = MFunc->getISel()->getVRegMap(); + assert(vreg_map_from_isel.count(arg) && "Argument not found in ISel's vreg_map!"); // 1. 获取该 Argument 对象对应的虚拟寄存器 unsigned vreg = isel->getVReg(arg); @@ -58,6 +76,9 @@ void RISCv64RegAlloc::handleCallingConvention() { } } +/** + * @brief 消除帧索引,为局部变量和栈参数分配栈偏移量,并展开伪指令。 + */ void RISCv64RegAlloc::eliminateFrameIndices() { StackFrameInfo& frame_info = MFunc->getFrameInfo(); // 初始偏移量,为保存ra和s0留出空间。 @@ -185,30 +206,38 @@ void RISCv64RegAlloc::eliminateFrameIndices() { } } +/** + * @brief 计算给定 MachineInstr 的 Use (读取) 和 Def (写入) 寄存器集合。 + * 这是活跃性分析的基础。 + * @param instr 要分析的机器指令。 + * @param use 存储 Use 寄存器(虚拟寄存器 ID)的集合。 + * @param def 存储 Def 寄存器(虚拟寄存器 ID)的集合。 + */ void RISCv64RegAlloc::getInstrUseDef(MachineInstr* instr, LiveSet& use, LiveSet& def) { - bool is_def = true; + bool first_reg_is_def = true; // 默认情况下,指令的第一个寄存器操作数是定义 (def) auto opcode = instr->getOpcode(); - // --- MODIFICATION START: 细化对指令的 use/def 定义 --- - - // 对于没有定义目标寄存器的指令,预先设置 is_def = false + // 1. 特殊指令的 `is_def` 标志调整 + // 这些指令的第一个寄存器操作数是源操作数 (use),而不是目标操作数 (def)。 if (opcode == RVOpcodes::SW || opcode == RVOpcodes::SD || opcode == RVOpcodes::BEQ || opcode == RVOpcodes::BNE || opcode == RVOpcodes::BLT || opcode == RVOpcodes::BGE || opcode == RVOpcodes::BLTU || opcode == RVOpcodes::BGEU || opcode == RVOpcodes::RET || opcode == RVOpcodes::J) { - is_def = false; + first_reg_is_def = false; + } + + // JAL 和 JALR 指令定义 ra (x1) + if (opcode == RVOpcodes::JAL || opcode == RVOpcodes::JALR) { + // 使用 ra 对应的特殊虚拟寄存器ID + def.insert(static_cast(PhysicalReg::RA)); + first_reg_is_def = false; // JAL/JALR 的第一个操作数是 ra,已经处理为 def } - // 对 CALL 指令进行特殊处理 + // 2. CALL 指令的特殊处理 if (opcode == RVOpcodes::CALL) { - // CALL 指令的第一个操作数通常是目标函数标签,不是寄存器。 - // 它可能会有一个可选的返回值(def),以及一系列参数(use)。 - // 这里的处理假定 CALL 的机器指令操作数布局是: - // [可选: dest_vreg (def)], [函数标签], [可选: arg1_vreg (use)], [可选: arg2_vreg (use)], ... - - // 我们需要一种方法来识别哪些操作数是def,哪些是use。 - // 一个简单的约定:如果第一个操作数是寄存器,则它是def(返回值)。 + // 1.1 处理返回值 (def) + // 约定:如果CALL指令有返回值,IR阶段会将返回值vreg作为指令的第一个操作数。 if (!instr->getOperands().empty() && instr->getOperands().front()->getKind() == MachineOperand::KIND_REG) { auto reg_op = static_cast(instr->getOperands().front().get()); if (reg_op->isVirtual()) { @@ -216,14 +245,19 @@ void RISCv64RegAlloc::getInstrUseDef(MachineInstr* instr, LiveSet& use, LiveSet& } } - // 遍历所有操作数,非第一个寄存器操作数均视为use - bool first_reg_skipped = false; + // 1.2 处理参数 (use) + // 参数通常是指令的后续操作数 + bool first_operand_processed = false; // 用于跳过已作为def处理的返回值 for (const auto& op : instr->getOperands()) { if (op->getKind() == MachineOperand::KIND_REG) { - if (!first_reg_skipped) { - first_reg_skipped = true; - continue; // 跳过我们已经作为def处理的返回值 + if (!first_operand_processed) { // 如果是第一个操作数 + first_operand_processed = true; + // 如果第一个操作数是返回值(已被加入def),则跳过 + if (def.count(static_cast(op.get())->getVRegNum())) { + continue; + } } + // 否则,该寄存器是 use auto reg_op = static_cast(op.get()); if (reg_op->isVirtual()) { use.insert(reg_op->getVRegNum()); @@ -231,34 +265,43 @@ void RISCv64RegAlloc::getInstrUseDef(MachineInstr* instr, LiveSet& use, LiveSet& } } - // **重要**: CALL指令还隐式定义(杀死)了所有调用者保存的寄存器。 - // 一个完整的实现会在这里将所有caller-saved寄存器标记为def, - // 以确保任何跨调用存活的变量都不会被分配到这些寄存器中。 - // 这个简化的实现暂不处理隐式def,但这是未来优化的关键点。 + // **重要**: CALL指令隐式定义(杀死)了所有调用者保存的寄存器。 + // **这部分逻辑不在getInstrUseDef中直接处理**。 + // 而是通过`buildInterferenceGraph`中添加物理寄存器节点与活跃虚拟寄存器之间的干扰边来完成。 + // 这样 Liveness Analysis 可以在虚拟寄存器层面进行,而物理寄存器干扰的复杂性则留给干扰图。 return; // CALL 指令处理完毕,直接返回 } - // --- MODIFICATION END --- - - // 对其他所有指令的通用处理逻辑 + // 3. 对其他所有指令的通用处理逻辑 for (const auto& op : instr->getOperands()) { if (op->getKind() == MachineOperand::KIND_REG) { auto reg_op = static_cast(op.get()); - if (reg_op->isVirtual()) { - if (is_def) { + if (reg_op->isVirtual()) { // 只有虚拟寄存器才需要处理 Use/Def + // 如果是第一个寄存器操作数,且指令类型表明它是定义 (def),则加入 def 集合 + // 否则,它是 use (读取) + if (first_reg_is_def) { def.insert(reg_op->getVRegNum()); - is_def = false; // 一条指令通常只有一个目标寄存ator + first_reg_is_def = false; // 确保每条指令只定义一个目标寄存器 } else { use.insert(reg_op->getVRegNum()); } } } else if (op->getKind() == MachineOperand::KIND_MEM) { - // 内存操作数 `offset(base)` 中的 base 寄存器是 use + // 内存操作数 `offset(base)` 中的 `base` 寄存器是 `use` auto mem_op = static_cast(op.get()); if (mem_op->getBase()->isVirtual()) { use.insert(mem_op->getBase()->getVRegNum()); } + // 对于存储内存指令 (SW, SD),要存储的值(第一个操作数)也是 `use` + if ((opcode == RVOpcodes::SW || opcode == RVOpcodes::SD) && + !instr->getOperands().empty() && // 确保有操作数 + instr->getOperands().front()->getKind() == MachineOperand::KIND_REG) { // 且第一个操作数是寄存器 + auto src_reg_op = static_cast(instr->getOperands().front().get()); + if (src_reg_op->isVirtual()) { + use.insert(src_reg_op->getVRegNum()); + } + } } } } @@ -344,6 +387,7 @@ void RISCv64RegAlloc::analyzeLiveness() { void RISCv64RegAlloc::buildInterferenceGraph() { std::set all_vregs; + // 收集所有虚拟寄存器和物理寄存器在干扰图中的节点ID for (auto& mbb : MFunc->getBlocks()) { for(auto& instr : mbb->getInstructions()) { LiveSet use, def; @@ -352,6 +396,11 @@ void RISCv64RegAlloc::buildInterferenceGraph() { for(auto d : def) all_vregs.insert(d); } } + // 添加所有物理寄存器对应的特殊虚拟寄存器ID到all_vregs,作为干扰图节点 + for (auto preg : allocable_int_regs) { + all_vregs.insert(preg_to_vreg_id_map.at(preg)); + } + for (auto vreg : all_vregs) { interference_graph[vreg] = {}; } @@ -361,6 +410,7 @@ void RISCv64RegAlloc::buildInterferenceGraph() { getInstrUseDef(instr.get(), use, def); const LiveSet& live_out = live_out_map.at(instr.get()); + // 标准干扰图构建:def 与 live_out 中的其他变量干扰 for (unsigned d : def) { for (unsigned l : live_out) { if (d != l) { @@ -369,6 +419,24 @@ void RISCv64RegAlloc::buildInterferenceGraph() { } } } + + // *** 核心修改点:处理 CALL 指令的隐式 def *** + if (instr->getOpcode() == RVOpcodes::CALL) { + // CALL 指令会定义(杀死)所有调用者保存的寄存器。 + // 因此,所有调用者保存的物理寄存器都与 CALL 指令的 live_out 中的所有变量冲突。 + const std::vector& caller_saved_regs = getCallerSavedIntRegs(); + for (PhysicalReg cs_reg : caller_saved_regs) { + unsigned cs_vreg_id = preg_to_vreg_id_map.at(cs_reg); // 获取物理寄存器对应的特殊vreg ID + + // 将这个物理寄存器节点与 CALL 指令的 live_out 中的所有虚拟寄存器添加干扰边。 + for (unsigned live_vreg_out : live_out) { + if (cs_vreg_id != live_vreg_out) { // 避免自己和自己干扰 + interference_graph[cs_vreg_id].insert(live_vreg_out); + interference_graph[live_vreg_out].insert(cs_vreg_id); + } + } + } + } } } } diff --git a/src/include/RISCv64ISel.h b/src/include/RISCv64ISel.h index 472edfe..cb73228 100644 --- a/src/include/RISCv64ISel.h +++ b/src/include/RISCv64ISel.h @@ -17,6 +17,8 @@ public: // 公开接口,以便后续模块(如RegAlloc)可以查询或创建vreg unsigned getVReg(Value* val); unsigned getNewVReg() { return vreg_counter++; } + // 获取 vreg_map 的公共接口 + const std::map& getVRegMap() const { return vreg_map; } private: // DAG节点定义,作为ISel的内部实现细节 diff --git a/src/include/RISCv64LLIR.h b/src/include/RISCv64LLIR.h index 0331d78..3f68566 100644 --- a/src/include/RISCv64LLIR.h +++ b/src/include/RISCv64LLIR.h @@ -35,7 +35,12 @@ enum class PhysicalReg { // (保持您原有的 F0-F31 命名) F0, F1, F2, F3, F4, F5, F6, F7, F8, F9, F10, F11, F12, F13, F14, F15, F16, F17, F18, F19, F20, F21, - F22, F23, F24, F25, F26, F27, F28, F29, F30, F31 + F22, F23, F24, F25, F26, F27, F28, F29, F30, F31, + + // 用于内部表示物理寄存器在干扰图中的节点ID(一个简单的特殊ID,确保不与vreg_counter冲突) + // 假设 vreg_counter 不会达到这么大的值 + PHYS_REG_START_ID = 10000, + PHYS_REG_END_ID = PHYS_REG_START_ID + 32, // 预留足够的空间 }; // RISC-V 指令操作码枚举 @@ -67,6 +72,9 @@ enum class RVOpcodes { FRAME_ADDR, // 获取栈帧变量的地址 }; +// 定义一个全局辅助函数或常量,提供调用者保存寄存器列表 +const std::vector& getCallerSavedIntRegs(); + class MachineOperand; class RegOperand; class ImmOperand; @@ -215,6 +223,15 @@ private: StackFrameInfo frame_info; }; +inline const std::vector& getCallerSavedIntRegs() { + static const std::vector regs = { + PhysicalReg::T0, PhysicalReg::T1, PhysicalReg::T2, PhysicalReg::T3, + PhysicalReg::T4, PhysicalReg::T5, PhysicalReg::T6, + PhysicalReg::A0, PhysicalReg::A1, PhysicalReg::A2, PhysicalReg::A3, + PhysicalReg::A4, PhysicalReg::A5, PhysicalReg::A6, PhysicalReg::A7 + }; + return regs; +} } // namespace sysy #endif // RISCV64_LLIR_H \ No newline at end of file diff --git a/src/include/RISCv64RegAlloc.h b/src/include/RISCv64RegAlloc.h index 6fbecb5..9af7d4a 100644 --- a/src/include/RISCv64RegAlloc.h +++ b/src/include/RISCv64RegAlloc.h @@ -2,6 +2,7 @@ #define RISCV64_REGALLOC_H #include "RISCv64LLIR.h" +#include "RISCv64ISel.h" // 包含 RISCv64ISel.h 以访问 ISel 和 Value 类型 namespace sysy { @@ -56,6 +57,7 @@ private: // 存储vreg到IR Value*的反向映射 // 这个map将在run()函数开始时被填充,并在rewriteFunction()中使用。 std::map vreg_to_value_map; + std::map preg_to_vreg_id_map; // 物理寄存器到特殊vreg ID的映射 // 用于计算类型大小的辅助函数 unsigned getTypeSizeInBytes(Type* type);